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