001/**
002 *
003 * Copyright the original author or authors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.pubsub;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.List;
022
023import org.jivesoftware.smack.SmackException.NoResponseException;
024import org.jivesoftware.smack.SmackException.NotConnectedException;
025import org.jivesoftware.smack.XMPPConnection;
026import org.jivesoftware.smack.XMPPException.XMPPErrorException;
027import org.jivesoftware.smack.packet.IQ.Type;
028import org.jivesoftware.smackx.disco.packet.DiscoverItems;
029import org.jivesoftware.smackx.pubsub.packet.PubSub;
030
031/**
032 * The main class for the majority of pubsub functionality.  In general
033 * almost all pubsub capabilities are related to the concept of a node.
034 * All items are published to a node, and typically subscribed to by other
035 * users.  These users then retrieve events based on this subscription.
036 * 
037 * @author Robin Collier
038 */
039public class LeafNode extends Node
040{
041        LeafNode(XMPPConnection connection, String nodeName)
042        {
043                super(connection, nodeName);
044        }
045        
046        /**
047         * Get information on the items in the node in standard
048         * {@link DiscoverItems} format.
049         * 
050         * @return The item details in {@link DiscoverItems} format
051         * @throws XMPPErrorException 
052         * @throws NoResponseException if there was no response from the server.
053         * @throws NotConnectedException 
054         */
055        public DiscoverItems discoverItems() throws NoResponseException, XMPPErrorException, NotConnectedException
056        {
057                DiscoverItems items = new DiscoverItems();
058                items.setTo(to);
059                items.setNode(getId());
060                return (DiscoverItems) con.createPacketCollectorAndSend(items).nextResultOrThrow();
061        }
062
063        /**
064         * Get the current items stored in the node.
065         * 
066         * @return List of {@link Item} in the node
067         * @throws XMPPErrorException
068         * @throws NoResponseException if there was no response from the server.
069         * @throws NotConnectedException 
070         */
071        @SuppressWarnings("unchecked")
072        public <T extends Item> List<T> getItems() throws NoResponseException, XMPPErrorException, NotConnectedException
073        {
074                PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId()));
075                
076                PubSub result = (PubSub) con.createPacketCollectorAndSend(request).nextResultOrThrow();
077                ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
078                return (List<T>)itemsElem.getItems();
079        }
080        
081        /**
082         * Get the current items stored in the node based
083         * on the subscription associated with the provided 
084         * subscription id.
085         * 
086         * @param subscriptionId -  The subscription id for the 
087         * associated subscription.
088         * @return List of {@link Item} in the node
089         * @throws XMPPErrorException
090         * @throws NoResponseException if there was no response from the server.
091         * @throws NotConnectedException 
092         */
093        @SuppressWarnings("unchecked")
094        public <T extends Item> List<T> getItems(String subscriptionId) throws NoResponseException, XMPPErrorException, NotConnectedException
095        {
096                PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), subscriptionId));
097                
098                PubSub result = (PubSub) con.createPacketCollectorAndSend(request).nextResultOrThrow();
099                ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
100                return (List<T>)itemsElem.getItems();
101        }
102
103        /**
104         * Get the items specified from the node.  This would typically be
105         * used when the server does not return the payload due to size 
106         * constraints.  The user would be required to retrieve the payload 
107         * after the items have been retrieved via {@link #getItems()} or an
108         * event, that did not include the payload.
109         * 
110         * @param ids Item ids of the items to retrieve
111         * 
112         * @return The list of {@link Item} with payload
113         * @throws XMPPErrorException 
114         * @throws NoResponseException if there was no response from the server.
115         * @throws NotConnectedException 
116         */
117        @SuppressWarnings("unchecked")
118        public <T extends Item> List<T> getItems(Collection<String> ids) throws NoResponseException, XMPPErrorException, NotConnectedException
119        {
120                List<Item> itemList = new ArrayList<Item>(ids.size());
121                
122                for (String id : ids)
123                {
124                        itemList.add(new Item(id));
125                }
126                PubSub request = createPubsubPacket(Type.GET, new ItemsExtension(ItemsExtension.ItemsElementType.items, getId(), itemList));
127                
128                PubSub result = (PubSub) con.createPacketCollectorAndSend(request).nextResultOrThrow();
129                ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
130                return (List<T>)itemsElem.getItems();
131        }
132
133        /**
134         * Get items persisted on the node, limited to the specified number.
135         * 
136         * @param maxItems Maximum number of items to return
137         * 
138         * @return List of {@link Item}
139         * @throws XMPPErrorException
140         * @throws NoResponseException if there was no response from the server.
141         * @throws NotConnectedException 
142         */
143        @SuppressWarnings("unchecked")
144        public <T extends Item> List<T> getItems(int maxItems) throws NoResponseException, XMPPErrorException, NotConnectedException
145        {
146                PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), maxItems));
147                
148                PubSub result = (PubSub) con.createPacketCollectorAndSend(request).nextResultOrThrow();
149                ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
150                return (List<T>)itemsElem.getItems();
151        }
152        
153        /**
154         * Get items persisted on the node, limited to the specified number
155         * based on the subscription associated with the provided subscriptionId.
156         * 
157         * @param maxItems Maximum number of items to return
158         * @param subscriptionId The subscription which the retrieval is based
159         * on.
160         * 
161         * @return List of {@link Item}
162         * @throws XMPPErrorException
163         * @throws NoResponseException if there was no response from the server.
164         * @throws NotConnectedException 
165         */
166        @SuppressWarnings("unchecked")
167        public <T extends Item> List<T> getItems(int maxItems, String subscriptionId) throws NoResponseException, XMPPErrorException, NotConnectedException
168        {
169                PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), subscriptionId, maxItems));
170                
171                PubSub result = (PubSub) con.createPacketCollectorAndSend(request).nextResultOrThrow();
172                ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
173                return (List<T>)itemsElem.getItems();
174        }
175        
176        /**
177         * Publishes an event to the node.  This is an empty event
178         * with no item.
179         * 
180         * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false
181         * and {@link ConfigureForm#isDeliverPayloads()}=false.
182         * 
183         * This is an asynchronous call which returns as soon as the 
184         * packet has been sent.
185         * 
186         * For synchronous calls use {@link #send() send()}.
187         * @throws NotConnectedException 
188         */
189        public void publish() throws NotConnectedException
190        {
191                PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));
192                
193                con.sendPacket(packet);
194        }
195        
196        /**
197         * Publishes an event to the node.  This is a simple item
198         * with no payload.
199         * 
200         * If the id is null, an empty item (one without an id) will be sent.
201         * Please note that this is not the same as {@link #send()}, which
202         * publishes an event with NO item.
203         * 
204         * This is an asynchronous call which returns as soon as the 
205         * packet has been sent.
206         * 
207         * For synchronous calls use {@link #send(Item) send(Item))}.
208         * 
209         * @param item - The item being sent
210         * @throws NotConnectedException 
211         */
212        @SuppressWarnings("unchecked")
213        public <T extends Item> void publish(T item) throws NotConnectedException
214        {
215                Collection<T> items = new ArrayList<T>(1);
216                items.add((T)(item == null ? new Item() : item));
217                publish(items);
218        }
219
220        /**
221         * Publishes multiple events to the node.  Same rules apply as in {@link #publish(Item)}.
222         * 
223         * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input
224         * list will get stored on the node, assuming it stores the last sent item.
225         * 
226         * This is an asynchronous call which returns as soon as the 
227         * packet has been sent.
228         * 
229         * For synchronous calls use {@link #send(Collection) send(Collection))}.
230         * 
231         * @param items - The collection of items being sent
232         * @throws NotConnectedException 
233         */
234        public <T extends Item> void publish(Collection<T> items) throws NotConnectedException
235        {
236                PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));
237                
238                con.sendPacket(packet);
239        }
240
241        /**
242         * Publishes an event to the node.  This is an empty event
243         * with no item.
244         * 
245         * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false
246         * and {@link ConfigureForm#isDeliverPayloads()}=false.
247         * 
248         * This is a synchronous call which will throw an exception 
249         * on failure.
250         * 
251         * For asynchronous calls, use {@link #publish() publish()}.
252         * @throws XMPPErrorException 
253         * @throws NoResponseException 
254         * @throws NotConnectedException 
255         * 
256         */
257        public void send() throws NoResponseException, XMPPErrorException, NotConnectedException
258        {
259                PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));
260                
261                con.createPacketCollectorAndSend(packet).nextResultOrThrow();
262        }
263        
264        /**
265         * Publishes an event to the node.  This can be either a simple item
266         * with no payload, or one with it.  This is determined by the Node
267         * configuration.
268         * 
269         * If the node has <b>deliver_payload=false</b>, the Item must not
270         * have a payload.
271         * 
272         * If the id is null, an empty item (one without an id) will be sent.
273         * Please note that this is not the same as {@link #send()}, which
274         * publishes an event with NO item.
275         * 
276         * This is a synchronous call which will throw an exception 
277         * on failure.
278         * 
279         * For asynchronous calls, use {@link #publish(Item) publish(Item)}.
280         * 
281         * @param item - The item being sent
282         * @throws XMPPErrorException 
283         * @throws NoResponseException 
284         * @throws NotConnectedException 
285         * 
286         */
287        @SuppressWarnings("unchecked")
288        public <T extends Item> void send(T item) throws NoResponseException, XMPPErrorException, NotConnectedException
289        {
290                Collection<T> items = new ArrayList<T>(1);
291                items.add((item == null ? (T)new Item() : item));
292                send(items);
293        }
294        
295        /**
296         * Publishes multiple events to the node.  Same rules apply as in {@link #send(Item)}.
297         * 
298         * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input
299         * list will get stored on the node, assuming it stores the last sent item.
300         *  
301         * This is a synchronous call which will throw an exception 
302         * on failure.
303         * 
304         * For asynchronous calls, use {@link #publish(Collection) publish(Collection))}.
305         * 
306         * @param items - The collection of {@link Item} objects being sent
307         * @throws XMPPErrorException 
308         * @throws NoResponseException 
309         * @throws NotConnectedException 
310         * 
311         */
312        public <T extends Item> void send(Collection<T> items) throws NoResponseException, XMPPErrorException, NotConnectedException
313        {
314                PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));
315                
316                con.createPacketCollectorAndSend(packet).nextResultOrThrow();
317        }
318        
319        /**
320         * Purges the node of all items.
321         *   
322         * <p>Note: Some implementations may keep the last item
323         * sent.
324         * @throws XMPPErrorException 
325         * @throws NoResponseException if there was no response from the server.
326         * @throws NotConnectedException 
327         */
328        public void deleteAllItems() throws NoResponseException, XMPPErrorException, NotConnectedException
329        {
330                PubSub request = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PURGE_OWNER, getId()), PubSubElementType.PURGE_OWNER.getNamespace());
331                
332                con.createPacketCollectorAndSend(request).nextResultOrThrow();
333        }
334        
335        /**
336         * Delete the item with the specified id from the node.
337         * 
338         * @param itemId The id of the item
339         * @throws XMPPErrorException 
340         * @throws NoResponseException 
341         * @throws NotConnectedException 
342         */
343        public void deleteItem(String itemId) throws NoResponseException, XMPPErrorException, NotConnectedException
344        {
345                Collection<String> items = new ArrayList<String>(1);
346                items.add(itemId);
347                deleteItem(items);
348        }
349        
350        /**
351         * Delete the items with the specified id's from the node.
352         * 
353         * @param itemIds The list of id's of items to delete
354         * @throws XMPPErrorException
355         * @throws NoResponseException if there was no response from the server.
356         * @throws NotConnectedException 
357         */
358        public void deleteItem(Collection<String> itemIds) throws NoResponseException, XMPPErrorException, NotConnectedException
359        {
360                List<Item> items = new ArrayList<Item>(itemIds.size());
361                
362                for (String id : itemIds)
363                {
364                        items.add(new Item(id));
365                }
366                PubSub request = createPubsubPacket(Type.SET, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items));
367                con.createPacketCollectorAndSend(request).nextResultOrThrow();
368        }
369}