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