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.form.ConfigureForm; 031import org.jivesoftware.smackx.pubsub.packet.PubSub; 032 033/** 034 * The main class for the majority of PubSub functionality. In general 035 * almost all PubSub capabilities are related to the concept of a node. 036 * All items are published to a node, and typically subscribed to by other 037 * users. These users then retrieve events based on this subscription. 038 * 039 * @author Robin Collier 040 */ 041public class LeafNode extends Node { 042 LeafNode(PubSubManager pubSubManager, String nodeId) { 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 if there was an XMPP error returned. 052 * @throws NoResponseException if there was no response from the server. 053 * @throws NotConnectedException if the XMPP connection is not connected. 054 * @throws InterruptedException if the calling thread was interrupted. 055 */ 056 public DiscoverItems discoverItems() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 057 DiscoverItems items = new DiscoverItems(); 058 items.setTo(pubSubManager.getServiceJid()); 059 items.setNode(getId()); 060 return pubSubManager.getConnection().createStanzaCollectorAndSend(items).nextResultOrThrow(); 061 } 062 063 /** 064 * Get the current items stored in the node. 065 * 066 * @param <T> type of the items. 067 * @return List of {@link Item} in the node 068 * @throws XMPPErrorException if there was an XMPP error returned. 069 * @throws NoResponseException if there was no response from the server. 070 * @throws NotConnectedException if the XMPP connection is not connected. 071 * @throws InterruptedException if the calling thread was interrupted. 072 */ 073 public <T extends Item> List<T> getItems() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 074 return getItems((List<ExtensionElement>) null, 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 * @param <T> type of the items. 085 * 086 * @return List of {@link Item} in the node 087 * @throws XMPPErrorException if there was an XMPP error returned. 088 * @throws NoResponseException if there was no response from the server. 089 * @throws NotConnectedException if the XMPP connection is not connected. 090 * @throws InterruptedException if the calling thread was interrupted. 091 */ 092 public <T extends Item> List<T> getItems(String subscriptionId) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 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 * @param <T> type of the items. 106 * 107 * @return The list of {@link Item} with payload 108 * @throws XMPPErrorException if there was an XMPP error returned. 109 * @throws NoResponseException if there was no response from the server. 110 * @throws NotConnectedException if the XMPP connection is not connected. 111 * @throws InterruptedException if the calling thread was interrupted. 112 */ 113 public <T extends Item> List<T> getItems(Collection<String> ids) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 114 List<Item> itemList = new ArrayList<>(ids.size()); 115 116 for (String id : ids) { 117 itemList.add(new Item(id)); 118 } 119 PubSub request = createPubsubPacket(Type.get, new ItemsExtension(ItemsExtension.ItemsElementType.items, getId(), itemList)); 120 return getItems(request); 121 } 122 123 /** 124 * Get items persisted on the node, limited to the specified number. 125 * 126 * @param maxItems Maximum number of items to return 127 * @param <T> type of the items. 128 * 129 * @return List of {@link Item} 130 * @throws XMPPErrorException if there was an XMPP error returned. 131 * @throws NoResponseException if there was no response from the server. 132 * @throws NotConnectedException if the XMPP connection is not connected. 133 * @throws InterruptedException if the calling thread was interrupted. 134 */ 135 public <T extends Item> List<T> getItems(int maxItems) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 136 PubSub request = createPubsubPacket(Type.get, new GetItemsRequest(getId(), maxItems)); 137 return getItems(request); 138 } 139 140 /** 141 * Get items persisted on the node, limited to the specified number 142 * based on the subscription associated with the provided subscriptionId. 143 * 144 * @param maxItems Maximum number of items to return 145 * @param subscriptionId The subscription which the retrieval is based 146 * on. 147 * 148 * @return List of {@link Item} 149 * @param <T> type of the items. 150 * 151 * @throws XMPPErrorException if there was an XMPP error returned. 152 * @throws NoResponseException if there was no response from the server. 153 * @throws NotConnectedException if the XMPP connection is not connected. 154 * @throws InterruptedException if the calling thread was interrupted. 155 */ 156 public <T extends Item> List<T> getItems(int maxItems, String subscriptionId) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 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 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 * @param <T> type of the items. 173 * 174 * @return List of {@link Item} 175 * @throws NoResponseException if there was no response from the remote entity. 176 * @throws XMPPErrorException if there was an XMPP error returned. 177 * @throws NotConnectedException if the XMPP connection is not connected. 178 * @throws InterruptedException if the calling thread was interrupted. 179 */ 180 public <T extends Item> List<T> getItems(List<ExtensionElement> additionalExtensions, 181 List<ExtensionElement> returnedExtensions) throws NoResponseException, 182 XMPPErrorException, NotConnectedException, InterruptedException { 183 PubSub request = createPubsubPacket(Type.get, new GetItemsRequest(getId())); 184 request.addExtensions(additionalExtensions); 185 return getItems(request, returnedExtensions); 186 } 187 188 private <T extends Item> List<T> getItems(PubSub request) throws NoResponseException, 189 XMPPErrorException, NotConnectedException, InterruptedException { 190 return getItems(request, null); 191 } 192 193 @SuppressWarnings("unchecked") 194 private <T extends Item> List<T> getItems(PubSub request, 195 List<ExtensionElement> returnedExtensions) throws NoResponseException, 196 XMPPErrorException, NotConnectedException, InterruptedException { 197 PubSub result = pubSubManager.getConnection().createStanzaCollectorAndSend(request).nextResultOrThrow(); 198 ItemsExtension itemsElem = result.getExtension(PubSubElementType.ITEMS); 199 if (returnedExtensions != null) { 200 returnedExtensions.addAll(result.getExtensions()); 201 } 202 return (List<T>) itemsElem.getItems(); 203 } 204 205 /** 206 * Publishes an event to the node. This is an empty event 207 * with no item. 208 * 209 * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false 210 * and {@link ConfigureForm#isDeliverPayloads()}=false. 211 * 212 * @throws NotConnectedException if the XMPP connection is not connected. 213 * @throws InterruptedException if the calling thread was interrupted. 214 * @throws XMPPErrorException if there was an XMPP error returned. 215 * @throws NoResponseException if there was no response from the remote entity. 216 * @deprecated use {@link #publish()} instead. 217 */ 218 @Deprecated 219 public void send() throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException { 220 publish(); 221 } 222 223 /** 224 * Publishes an event to the node. This is a simple item 225 * with no payload. 226 * 227 * If the id is null, an empty item (one without an id) will be sent. 228 * Please note that this is not the same as {@link #send()}, which 229 * publishes an event with NO item. 230 * 231 * @param item - The item being sent 232 * @param <T> type of the items. 233 * 234 * @throws NotConnectedException if the XMPP connection is not connected. 235 * @throws InterruptedException if the calling thread was interrupted. 236 * @throws XMPPErrorException if there was an XMPP error returned. 237 * @throws NoResponseException if there was no response from the remote entity. 238 * @deprecated use {@link #publish(Item)} instead. 239 */ 240 @Deprecated 241 public <T extends Item> void send(T item) throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException { 242 publish(item); 243 } 244 245 /** 246 * Publishes multiple events to the node. Same rules apply as in {@link #publish(Item)}. 247 * 248 * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input 249 * list will get stored on the node, assuming it stores the last sent item. 250 * 251 * @param items - The collection of items being sent 252 * @param <T> type of the items. 253 * 254 * @throws NotConnectedException if the XMPP connection is not connected. 255 * @throws InterruptedException if the calling thread was interrupted. 256 * @throws XMPPErrorException if there was an XMPP error returned. 257 * @throws NoResponseException if there was no response from the remote entity. 258 * @deprecated use {@link #publish(Collection)} instead. 259 */ 260 @Deprecated 261 public <T extends Item> void send(Collection<T> items) throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException { 262 publish(items); 263 } 264 265 /** 266 * Publishes an event to the node. This is an empty event 267 * with no item. 268 * 269 * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false 270 * and {@link ConfigureForm#isDeliverPayloads()}=false. 271 * 272 * @throws XMPPErrorException if there was an XMPP error returned. 273 * @throws NoResponseException if there was no response from the remote entity. 274 * @throws NotConnectedException if the XMPP connection is not connected. 275 * @throws InterruptedException if the calling thread was interrupted. 276 * 277 */ 278 public void publish() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 279 PubSub packet = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PUBLISH, getId())); 280 281 pubSubManager.getConnection().createStanzaCollectorAndSend(packet).nextResultOrThrow(); 282 } 283 284 /** 285 * Publishes an event to the node. This can be either a simple item 286 * with no payload, or one with it. This is determined by the Node 287 * configuration. 288 * 289 * If the node has <b>deliver_payload=false</b>, the Item must not 290 * have a payload. 291 * 292 * If the id is null, an empty item (one without an id) will be sent. 293 * Please note that this is not the same as {@link #send()}, which 294 * publishes an event with NO item. 295 * 296 * @param item - The item being sent 297 * @param <T> type of the items. 298 * 299 * @throws XMPPErrorException if there was an XMPP error returned. 300 * @throws NoResponseException if there was no response from the remote entity. 301 * @throws NotConnectedException if the XMPP connection is not connected. 302 * @throws InterruptedException if the calling thread was interrupted. 303 * 304 */ 305 @SuppressWarnings("unchecked") 306 public <T extends Item> void publish(T item) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 307 Collection<T> items = new ArrayList<>(1); 308 items.add(item == null ? (T) new Item() : item); 309 publish(items); 310 } 311 312 /** 313 * Publishes multiple events to the node. Same rules apply as in {@link #send(Item)}. 314 * 315 * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input 316 * list will get stored on the node, assuming it stores the last sent item. 317 * 318 * @param items - The collection of {@link Item} objects being sent 319 * @param <T> type of the items. 320 * 321 * @throws XMPPErrorException if there was an XMPP error returned. 322 * @throws NoResponseException if there was no response from the remote entity. 323 * @throws NotConnectedException if the XMPP connection is not connected. 324 * @throws InterruptedException if the calling thread was interrupted. 325 * 326 */ 327 public <T extends Item> void publish(Collection<T> items) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 328 PubSub packet = createPubsubPacket(Type.set, new PublishItem<>(getId(), items)); 329 330 pubSubManager.getConnection().createStanzaCollectorAndSend(packet).nextResultOrThrow(); 331 } 332 333 /** 334 * Purges the node of all items. 335 * 336 * <p>Note: Some implementations may keep the last item 337 * sent. 338 * @throws XMPPErrorException if there was an XMPP error returned. 339 * @throws NoResponseException if there was no response from the server. 340 * @throws NotConnectedException if the XMPP connection is not connected. 341 * @throws InterruptedException if the calling thread was interrupted. 342 */ 343 public void deleteAllItems() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 344 PubSub request = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PURGE_OWNER, getId())); 345 346 pubSubManager.getConnection().createStanzaCollectorAndSend(request).nextResultOrThrow(); 347 } 348 349 /** 350 * Delete the item with the specified id from the node. 351 * 352 * @param itemId The id of the item 353 * @throws XMPPErrorException if there was an XMPP error returned. 354 * @throws NoResponseException if there was no response from the remote entity. 355 * @throws NotConnectedException if the XMPP connection is not connected. 356 * @throws InterruptedException if the calling thread was interrupted. 357 */ 358 public void deleteItem(String itemId) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 359 Collection<String> items = new ArrayList<>(1); 360 items.add(itemId); 361 deleteItem(items); 362 } 363 364 /** 365 * Delete the items with the specified id's from the node. 366 * 367 * @param itemIds The list of id's of items to delete 368 * @throws XMPPErrorException if there was an XMPP error returned. 369 * @throws NoResponseException if there was no response from the server. 370 * @throws NotConnectedException if the XMPP connection is not connected. 371 * @throws InterruptedException if the calling thread was interrupted. 372 */ 373 public void deleteItem(Collection<String> itemIds) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 374 List<Item> items = new ArrayList<>(itemIds.size()); 375 376 for (String id : itemIds) { 377 items.add(new Item(id)); 378 } 379 PubSub request = createPubsubPacket(Type.set, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items)); 380 pubSubManager.getConnection().createStanzaCollectorAndSend(request).nextResultOrThrow(); 381 } 382}