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