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