SMACK-272 Add support for XEP-0060 (pubsub)

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@11346 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
rcollier 2009-10-28 22:38:40 +00:00
parent 92ba2d7f33
commit f7a1c750ad
73 changed files with 7214 additions and 0 deletions

View File

@ -124,6 +124,12 @@
<className>org.jivesoftware.smackx.provider.DelayInformationProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>delay</elementName>
<namespace>urn:xmpp:delay</namespace>
<className>org.jivesoftware.smackx.provider.DelayInfoProvider</className>
</extensionProvider>
<!-- Version -->
<iqProvider>
<elementName>query</elementName>
@ -472,4 +478,140 @@
<className>org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider</className>
</extensionProvider>
<!-- SHIM -->
<extensionProvider>
<elementName>headers</elementName>
<namespace>http://jabber.org/protocol/shim</namespace>
<className>org.jivesoftware.smackx.provider.HeadersProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>header</elementName>
<namespace>http://jabber.org/protocol/shim</namespace>
<className>org.jivesoftware.smackx.provider.HeaderProvider</className>
</extensionProvider>
<!-- XEP-0060 pubsub -->
<iqProvider>
<elementName>pubsub</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.PubSubProvider</className>
</iqProvider>
<extensionProvider>
<elementName>create</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>items</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.ItemsProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>item</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.ItemProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>subscriptions</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>subscription</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>affiliations</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>affiliation</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.AffiliationProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>options</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.provider.FormNodeProvider</className>
</extensionProvider>
<!-- XEP-0060 pubsub#owner -->
<iqProvider>
<elementName>pubsub</elementName>
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.PubSubProvider</className>
</iqProvider>
<extensionProvider>
<elementName>configure</elementName>
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
<className>org.jivesoftware.smackx.provider.FormNodeProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>default</elementName>
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
<className>org.jivesoftware.smackx.provider.FormNodeProvider</className>
</extensionProvider>
<!-- XEP-0060 pubsub#event -->
<extensionProvider>
<elementName>event</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.EventProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>configuration</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>delete</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>options</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.provider.FormNodeProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>items</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.ItemsProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>item</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.ItemProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>retract</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.RetractEventProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>purge</elementName>
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
</extensionProvider>
</smackProviders>

View File

@ -70,6 +70,11 @@
<td><a href="http://www.xmpp.org/extensions/xep-0096.html">XEP-0096</a></td>
<td>Transfer files between two users over XMPP.</td>
</tr>
<tr>
<td><a href="pubsub.html">PubSub</a></td>
<td><a href="http://www.xmpp.org/extensions/xep-0060.html">XEP-0060</a></td>
<td>Generic publish and subscribe functionality.</td>
</tr>
</table>
</body>
</html>

View File

@ -21,6 +21,7 @@
<a href="invitation.html">Group Chat Invitations</a><br>
<a href="disco.html">Service Discovery</a><br>
<a href="filetransfer.html">File Transfer</a><br>
<a href="pubsub.html">PubSub</a><br>
</p>
</body>

View File

@ -0,0 +1,38 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* This enumeration represents the access models for the pubsub node
* as defined in the pubsub specification section <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config">16.4.3</a>
*
* @author Robin Collier
*/
public enum AccessModel
{
/** Anyone may subscribe and retrieve items */
open,
/** Subscription request must be approved and only subscribers may retrieve items */
authorize,
/** Anyone with a presence subscription of both or from may subscribe and retrieve items */
presence,
/** Anyone in the specified roster group(s) may subscribe and retrieve items */
roster,
/** Only those on a whitelist may subscribe and retrieve items */
whitelist;
}

View File

@ -0,0 +1,90 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.PacketExtension;
/**
* Represents a affiliation between a user and a node, where the {@link #type} defines
* the type of affiliation.
*
* Affiliations are retrieved from the {@link PubSubManager#getAffiliations()} method, which
* gets affiliations for the calling user, based on the identity that is associated with
* the {@link XMPPConnection}.
*
* @author Robin Collier
*/
public class Affiliation implements PacketExtension
{
protected String node;
protected Type type;
public enum Type
{
member, none, outcast, owner, publisher
}
/**
* Constructs an affiliation.
*
* @param nodeId The node the user is affiliated with.
* @param affiliation The type of affiliation.
*/
public Affiliation(String nodeId, Type affiliation)
{
node = nodeId;
type = affiliation;
}
public String getNodeId()
{
return node;
}
public Type getType()
{
return type;
}
public String getElementName()
{
return "subscription";
}
public String getNamespace()
{
return null;
}
public String toXML()
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
appendAttribute(builder, "node", node);
appendAttribute(builder, "affiliation", type.toString());
builder.append("/>");
return builder.toString();
}
private void appendAttribute(StringBuilder builder, String att, String value)
{
builder.append(" ");
builder.append(att);
builder.append("='");
builder.append(value);
builder.append("'");
}
}

View File

@ -0,0 +1,69 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Collections;
import java.util.List;
/**
* Represents the <b>affiliations</b> element of the reply to a request for affiliations.
* It is defined in the specification in section <a href="http://xmpp.org/extensions/xep-0060.html#entity-affiliations">5.7 Retrieve Affiliations</a>.
*
* @author Robin Collier
*/
public class AffiliationsExtension extends NodeExtension
{
protected List<Affiliation> items = Collections.EMPTY_LIST;
public AffiliationsExtension()
{
super(PubSubElementType.AFFILIATIONS);
}
public AffiliationsExtension(List<Affiliation> subList)
{
super(PubSubElementType.AFFILIATIONS);
items = subList;
}
public List<Affiliation> getAffiliations()
{
return items;
}
@Override
public String toXML()
{
if ((items == null) || (items.size() == 0))
{
return super.toXML();
}
else
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
builder.append(">");
for (Affiliation item : items)
{
builder.append(item.toXML());
}
builder.append("</");
builder.append(getElementName());
builder.append(">");
return builder.toString();
}
}
}

View File

@ -0,0 +1,32 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* This enumeration represents the children association policy for associating leaf nodes
* with collection nodes as defined in the pubsub specification section <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config">16.4.3</a>
*
* @author Robin Collier
*/
public enum ChildrenAssociationPolicy
{
/** Anyone may associate leaf nodes with the collection */
all,
/** Only collection node owners may associate leaf nodes with the collection. */
owners,
/** Only those on a whitelist may associate leaf nodes with the collection. */
whitelist;
}

View File

@ -0,0 +1,15 @@
/*
* Created on 2009-07-13
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.XMPPConnection;
public class CollectionNode extends Node
{
CollectionNode(XMPPConnection connection, String nodeId)
{
super(connection, nodeId);
}
}

View File

@ -0,0 +1,56 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.packet.PacketExtension;
/**
* Represents the <b>configuration</b> element of a pubsub message event which
* associates a configuration form to the node which was configured. The form
* contains the current node configuration.
*
* @author Robin Collier
*/
public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketExtension
{
private ConfigureForm form;
public ConfigurationEvent(String nodeId)
{
super(PubSubElementType.CONFIGURATION, nodeId);
}
public ConfigurationEvent(String nodeId, ConfigureForm configForm)
{
super(PubSubElementType.CONFIGURATION, nodeId);
form = configForm;
}
public ConfigureForm getConfiguration()
{
return form;
}
public List<PacketExtension> getExtensions()
{
if (getConfiguration() == null)
return Collections.EMPTY_LIST;
else
return Arrays.asList(((PacketExtension)getConfiguration().getDataFormToSend()));
}
}

View File

@ -0,0 +1,707 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.FormField;
import org.jivesoftware.smackx.packet.DataForm;
/**
* A decorator for a {@link Form} to easily enable reading and updating
* of node configuration. All operations read or update the underlying {@link DataForm}.
*
* <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
* exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form
* if it does not already exist.
*
* @author Robin Collier
*/
public class ConfigureForm extends Form
{
/**
* Create a decorator from an existing {@link DataForm} that has been
* retrieved from parsing a node configuration request.
*
* @param configDataForm
*/
public ConfigureForm(DataForm configDataForm)
{
super(configDataForm);
}
/**
* Create a decorator from an existing {@link Form} for node configuration.
* Typically, this can be used to create a decorator for an answer form
* by using the result of {@link #createAnswerForm()} as the input parameter.
*
* @param nodeConfigForm
*/
public ConfigureForm(Form nodeConfigForm)
{
super(nodeConfigForm.getDataFormToSend());
}
/**
* Create a new form for configuring a node. This would typically only be used
* when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since
* configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and
* using the resulting form to create a answer form. See {@link #ConfigureForm(Form)}.
* @param formType
*/
public ConfigureForm(FormType formType)
{
super(formType.toString());
}
/**
* Get the currently configured {@link AccessModel}, null if it is not set.
*
* @return The current {@link AccessModel}
*/
public AccessModel getAccessModel()
{
String value = getFieldValue(ConfigureNodeFields.access_model);
if (value == null)
return null;
else
return AccessModel.valueOf(value);
}
/**
* Sets the value of access model.
*
* @param accessModel
*/
public void setAccessModel(AccessModel accessModel)
{
addField(ConfigureNodeFields.access_model, FormField.TYPE_LIST_SINGLE);
setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));
}
/**
* Returns the URL of an XSL transformation which can be applied to payloads in order to
* generate an appropriate message body element.
*
* @return URL to an XSL
*/
public String getBodyXSLT()
{
return getFieldValue(ConfigureNodeFields.body_xslt);
}
/**
* Set the URL of an XSL transformation which can be applied to payloads in order to
* generate an appropriate message body element.
*
* @param bodyXslt The URL of an XSL
*/
public void setBodyXSLT(String bodyXslt)
{
addField(ConfigureNodeFields.body_xslt, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);
}
/**
* The id's of the child nodes associated with a collection node (both leaf and collection).
*
* @return Iterator over the list of child nodes.
*/
public Iterator<String> getChildren()
{
return getFieldValues(ConfigureNodeFields.children);
}
/**
* Set the list of child node ids that are associated with a collection node.
*
* @param children
*/
public void setChildren(List<String> children)
{
addField(ConfigureNodeFields.children, FormField.TYPE_TEXT_MULTI);
setAnswer(ConfigureNodeFields.children.getFieldName(), children);
}
/**
* Returns the policy that determines who may associate children with the node.
*
* @return The current policy
*/
public ChildrenAssociationPolicy getChildrenAssociationPolicy()
{
String value = getFieldValue(ConfigureNodeFields.children_association_policy);
if (value == null)
return null;
else
return ChildrenAssociationPolicy.valueOf(value);
}
/**
* Sets the policy that determines who may associate children with the node.
*
* @param policy The policy being set
*/
public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy)
{
addField(ConfigureNodeFields.children_association_policy, FormField.TYPE_LIST_SINGLE);
setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), policy.toString());
}
/**
* Iterator of JID's that are on the whitelist that determines who can associate child nodes
* with the collection node. This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
* {@link ChildrenAssociationPolicy#whitelist}.
*
* @return Iterator over whitelist
*/
public Iterator<String> getChildrenAssociationWhitelist()
{
return getFieldValues(ConfigureNodeFields.children_association_whitelist);
}
/**
* Set the JID's in the whitelist of users that can associate child nodes with the collection
* node. This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
* {@link ChildrenAssociationPolicy#whitelist}.
*
* @param whitelist The list of JID's
*/
public void setChildrenAssociationWhitelist(List<String> whitelist)
{
addField(ConfigureNodeFields.children_association_whitelist, FormField.TYPE_JID_MULTI);
setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);
}
/**
* Gets the maximum number of child nodes that can be associated with the collection node.
*
* @return The maximum number of child nodes
*/
public int getChildrenMax()
{
return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));
}
/**
* Set the maximum number of child nodes that can be associated with a collection node.
*
* @param max The maximum number of child nodes.
*/
public void setChildrenMax(int max)
{
addField(ConfigureNodeFields.children_max, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);
}
/**
* Gets the collection node which the node is affiliated with.
*
* @return The collection node id
*/
public String getCollection()
{
return getFieldValue(ConfigureNodeFields.collection);
}
/**
* Sets the collection node which the node is affiliated with.
*
* @param collection The node id of the collection node
*/
public void setCollection(String collection)
{
addField(ConfigureNodeFields.collection, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);
}
/**
* Gets the URL of an XSL transformation which can be applied to the payload
* format in order to generate a valid Data Forms result that the client could
* display using a generic Data Forms rendering engine.
*
* @return The URL of an XSL transformation
*/
public String getDataformXSLT()
{
return getFieldValue(ConfigureNodeFields.dataform_xslt);
}
/**
* Sets the URL of an XSL transformation which can be applied to the payload
* format in order to generate a valid Data Forms result that the client could
* display using a generic Data Forms rendering engine.
*
* @param url The URL of an XSL transformation
*/
public void setDataformXSLT(String url)
{
addField(ConfigureNodeFields.dataform_xslt, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);
}
/**
* Does the node deliver payloads with event notifications.
*
* @return true if it does, false otherwise
*/
public boolean isDeliverPayloads()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));
}
/**
* Sets whether the node will deliver payloads with event notifications.
*
* @param deliver true if the payload will be delivered, false otherwise
*/
public void setDeliverPayloads(boolean deliver)
{
addField(ConfigureNodeFields.deliver_payloads, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
}
/**
* Determines who should get replies to items
*
* @return Who should get the reply
*/
public ItemReply getItemReply()
{
String value = getFieldValue(ConfigureNodeFields.itemreply);
if (value == null)
return null;
else
return ItemReply.valueOf(value);
}
/**
* Sets who should get the replies to items
*
* @param reply Defines who should get the reply
*/
public void setItemReply(ItemReply reply)
{
addField(ConfigureNodeFields.itemreply, FormField.TYPE_LIST_SINGLE);
setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));
}
/**
* Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
* true.
*
* @return The maximum number of items to persist
*/
public int getMaxItems()
{
return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));
}
/**
* Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
* true.
*
* @param max The maximum number of items to persist
*/
public void setMaxItems(int max)
{
addField(ConfigureNodeFields.max_items, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);
}
/**
* Gets the maximum payload size in bytes.
*
* @return The maximum payload size
*/
public int getMaxPayloadSize()
{
return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));
}
/**
* Sets the maximum payload size in bytes
*
* @param max The maximum payload size
*/
public void setMaxPayloadSize(int max)
{
addField(ConfigureNodeFields.max_payload_size, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);
}
/**
* Gets the node type
*
* @return The node type
*/
public NodeType getNodeType()
{
String value = getFieldValue(ConfigureNodeFields.node_type);
if (value == null)
return null;
else
return NodeType.valueOf(value);
}
/**
* Sets the node type
*
* @param type The node type
*/
public void setNodeType(NodeType type)
{
addField(ConfigureNodeFields.node_type, FormField.TYPE_LIST_SINGLE);
setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));
}
/**
* Determines if subscribers should be notified when the configuration changes.
*
* @return true if they should be notified, false otherwise
*/
public boolean isNotifyConfig()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.notify_config));
}
/**
* Sets whether subscribers should be notified when the configuration changes.
*
* @param notify true if subscribers should be notified, false otherwise
*/
public void setNotifyConfig(boolean notify)
{
addField(ConfigureNodeFields.notify_config, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);
}
/**
* Determines whether subscribers should be notified when the node is deleted.
*
* @return true if subscribers should be notified, false otherwise
*/
public boolean isNotifyDelete()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.notify_delete));
}
/**
* Sets whether subscribers should be notified when the node is deleted.
*
* @param notify true if subscribers should be notified, false otherwise
*/
public void setNotifyDelete(boolean notify)
{
addField(ConfigureNodeFields.notify_delete, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);
}
/**
* Determines whether subscribers should be notified when items are deleted
* from the node.
*
* @return true if subscribers should be notified, false otherwise
*/
public boolean isNotifyRetract()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.notify_retract));
}
/**
* Sets whether subscribers should be notified when items are deleted
* from the node.
*
* @param notify true if subscribers should be notified, false otherwise
*/
public void setNotifyRetract(boolean notify)
{
addField(ConfigureNodeFields.notify_retract, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);
}
/**
* Determines whether items should be persisted in the node.
*
* @return true if items are persisted
*/
public boolean isPersistItems()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.persist_items));
}
/**
* Sets whether items should be persisted in the node.
*
* @param persist true if items should be persisted, false otherwise
*/
public void setPersistentItems(boolean persist)
{
addField(ConfigureNodeFields.persist_items, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);
}
/**
* Determines whether to deliver notifications to available users only.
*
* @return true if users must be available
*/
public boolean isPresenceBasedDelivery()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));
}
/**
* Sets whether to deliver notifications to available users only.
*
* @param presenceBased true if user must be available, false otherwise
*/
public void setPresenceBasedDelivery(boolean presenceBased)
{
addField(ConfigureNodeFields.presence_based_delivery, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
}
/**
* Gets the publishing model for the node, which determines who may publish to it.
*
* @return The publishing model
*/
public PublishModel getPublishModel()
{
String value = getFieldValue(ConfigureNodeFields.publish_model);
if (value == null)
return null;
else
return PublishModel.valueOf(value);
}
/**
* Sets the publishing model for the node, which determines who may publish to it.
*
* @param publish The enum representing the possible options for the publishing model
*/
public void setPublishModel(PublishModel publish)
{
addField(ConfigureNodeFields.publish_model, FormField.TYPE_LIST_SINGLE);
setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));
}
/**
* Iterator over the multi user chat rooms that are specified as reply rooms.
*
* @return The reply room JID's
*/
public Iterator<String> getReplyRoom()
{
return getFieldValues(ConfigureNodeFields.replyroom);
}
/**
* Sets the multi user chat rooms that are specified as reply rooms.
*
* @param replyRooms The multi user chat room to use as reply rooms
*/
public void setReplyRoom(List<String> replyRooms)
{
addField(ConfigureNodeFields.replyroom, FormField.TYPE_LIST_MULTI);
setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);
}
/**
* Gets the specific JID's for reply to.
*
* @return The JID's
*/
public Iterator<String> getReplyTo()
{
return getFieldValues(ConfigureNodeFields.replyto);
}
/**
* Sets the specific JID's for reply to.
*
* @param replyTos The JID's to reply to
*/
public void setReplyTo(List<String> replyTos)
{
addField(ConfigureNodeFields.replyto, FormField.TYPE_LIST_MULTI);
setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);
}
/**
* Gets the roster groups that are allowed to subscribe and retrieve items.
*
* @return The roster groups
*/
public Iterator<String> getRosterGroupsAllowed()
{
return getFieldValues(ConfigureNodeFields.roster_groups_allowed);
}
/**
* Sets the roster groups that are allowed to subscribe and retrieve items.
*
* @param groups The roster groups
*/
public void setRosterGroupsAllowed(List<String> groups)
{
addField(ConfigureNodeFields.roster_groups_allowed, FormField.TYPE_LIST_MULTI);
setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
}
/**
* Determines if subscriptions are allowed.
*
* @return true if subscriptions are allowed, false otherwise
*/
public boolean isSubscibe()
{
return parseBoolean(getFieldValue(ConfigureNodeFields.subscribe));
}
/**
* Sets whether subscriptions are allowed.
*
* @param subscribe true if they are, false otherwise
*/
public void setSubscribe(boolean subscribe)
{
addField(ConfigureNodeFields.subscribe, FormField.TYPE_BOOLEAN);
setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
}
/**
* Gets the human readable node title.
*
* @return The node title
*/
public String getTitle()
{
return getFieldValue(ConfigureNodeFields.title);
}
/**
* Sets a human readable title for the node.
*
* @param title The node title
*/
public void setTitle(String title)
{
addField(ConfigureNodeFields.title, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.title.getFieldName(), title);
}
/**
* The type of node data, usually specified by the namespace of the payload (if any).
*
* @return The type of node data
*/
public String getDataType()
{
return getFieldValue(ConfigureNodeFields.type);
}
/**
* Sets the type of node data, usually specified by the namespace of the payload (if any).
*
* @param type The type of node data
*/
public void setDataType(String type)
{
addField(ConfigureNodeFields.type, FormField.TYPE_TEXT_SINGLE);
setAnswer(ConfigureNodeFields.type.getFieldName(), type);
}
@Override
public String toString()
{
StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
Iterator<FormField> fields = getFields();
while (fields.hasNext())
{
FormField formField = fields.next();
result.append('(');
result.append(formField.getVariable());
result.append(':');
Iterator<String> values = formField.getValues();
StringBuilder valuesBuilder = new StringBuilder();
while (values.hasNext())
{
if (valuesBuilder.length() > 0)
result.append(',');
String value = (String)values.next();
valuesBuilder.append(value);
}
if (valuesBuilder.length() == 0)
valuesBuilder.append("NOT SET");
result.append(valuesBuilder);
result.append(')');
}
result.append(']');
return result.toString();
}
static private boolean parseBoolean(String fieldValue)
{
return ("1".equals(fieldValue) || "true".equals(fieldValue));
}
private String getFieldValue(ConfigureNodeFields field)
{
FormField formField = getField(field.getFieldName());
return formField.getValues().next();
}
private Iterator<String> getFieldValues(ConfigureNodeFields field)
{
FormField formField = getField(field.getFieldName());
return formField.getValues();
}
private void addField(ConfigureNodeFields nodeField, String type)
{
String fieldName = nodeField.getFieldName();
if (getField(fieldName) == null)
{
FormField field = new FormField(fieldName);
field.setType(type);
addField(field);
}
}
private List<String> getListSingle(String value)
{
List<String> list = new ArrayList<String>(1);
list.add(value);
return list;
}
}

View File

@ -0,0 +1,218 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.net.URL;
import org.jivesoftware.smackx.Form;
/**
* This enumeration represents all the fields of a node configuration form. This enumeration
* is not required when using the {@link ConfigureForm} to configure nodes, but may be helpful
* for generic UI's using only a {@link Form} for configuration.
*
* @author Robin Collier
*/
public enum ConfigureNodeFields
{
/**
* Determines who may subscribe and retrieve items
*
* <p><b>Value: {@link AccessModel}</b></p>
*/
access_model,
/**
* The URL of an XSL transformation which can be applied to
* payloads in order to generate an appropriate message
* body element
*
* <p><b>Value: {@link URL}</b></p>
*/
body_xslt,
/**
* The collection with which a node is affiliated
*
* <p><b>Value: String</b></p>
*/
collection,
/**
* The URL of an XSL transformation which can be applied to
* payload format in order to generate a valid Data Forms result
* that the client could display using a generic Data Forms
* rendering engine body element.
*
* <p><b>Value: {@link URL}</b></p>
*/
dataform_xslt,
/**
* Whether to deliver payloads with event notifications
*
* <p><b>Value: boolean</b></p>
*/
deliver_payloads,
/**
* Whether owners or publisher should receive replies to items
*
* <p><b>Value: {@link ItemReply}</b></p>
*/
itemreply,
/**
* Who may associate leaf nodes with a collection
*
* <p><b>Value: {@link ChildrenAssociationPolicy}</b></p>
*/
children_association_policy,
/**
* The list of JIDs that may associate leaf nodes with a
* collection
*
* <p><b>Value: List of JIDs as Strings</b></p>
*/
children_association_whitelist,
/**
* The child nodes (leaf or collection) associated with a collection
*
* <p><b>Value: List of Strings</b></p>
*/
children,
/**
* The maximum number of child nodes that can be associated with a
* collection
*
* <p><b>Value: int</b></p>
*/
children_max,
/**
* The maximum number of items to persist
*
* <p><b>Value: int</b></p>
*/
max_items,
/**
* The maximum payload size in bytes
*
* <p><b>Value: int</b></p>
*/
max_payload_size,
/**
* Whether the node is a leaf (default) or collection
*
* <p><b>Value: {@link NodeType}</b></p>
*/
node_type,
/**
* Whether to notify subscribers when the node configuration changes
*
* <p><b>Value: boolean</b></p>
*/
notify_config,
/**
* Whether to notify subscribers when the node is deleted
*
* <p><b>Value: boolean</b></p>
*/
notify_delete,
/**
* Whether to notify subscribers when items are removed from the node
*
* <p><b>Value: boolean</b></p>
*/
notify_retract,
/**
* Whether to persist items to storage. This is required to have multiple
* items in the node.
*
* <p><b>Value: boolean</b></p>
*/
persist_items,
/**
* Whether to deliver notifications to available users only
*
* <p><b>Value: boolean</b></p>
*/
presence_based_delivery,
/**
* Defines who can publish to the node
*
* <p><b>Value: {@link PublishModel}</b></p>
*/
publish_model,
/**
* The specific multi-user chat rooms to specify for replyroom
*
* <p><b>Value: List of JIDs as Strings</b></p>
*/
replyroom,
/**
* The specific JID(s) to specify for replyto
*
* <p><b>Value: List of JIDs as Strings</b></p>
*/
replyto,
/**
* The roster group(s) allowed to subscribe and retrieve items
*
* <p><b>Value: List of strings</b></p>
*/
roster_groups_allowed,
/**
* Whether to allow subscriptions
*
* <p><b>Value: boolean</b></p>
*/
subscribe,
/**
* A friendly name for the node
*
* <p><b>Value: String</b></p>
*/
title,
/**
* The type of node data, ussually specified by the namespace
* of the payload(if any);MAY be a list-single rather than a
* text single
*
* <p><b>Value: String</b></p>
*/
type;
public String getFieldName()
{
return "pubsub#" + toString();
}
}

View File

@ -0,0 +1,45 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.List;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.util.PacketParserUtils;
/**
* This interface defines {@link PacketExtension} implementations that contain other
* extensions. This effectively extends the idea of an extension within one of the
* top level {@link Packet} types to consider any embedded element to be an extension
* of its parent. This more easily enables the usage of some of Smacks parsing
* utilities such as {@link PacketParserUtils#parsePacketExtension(String, String, org.xmlpull.v1.XmlPullParser)} to be used
* to parse any element of the XML being parsed.
*
* <p>Top level extensions have only one element, but they can have multiple children, or
* their children can have multiple children. This interface is a way of allowing extensions
* to be embedded within one another as a partial or complete one to one mapping of extension
* to element.
*
* @author Robin Collier
*/
public interface EmbeddedPacketExtension extends PacketExtension
{
/**
* Get the list of embedded {@link PacketExtension} objects.
*
* @return List of embedded {@link PacketExtension}
*/
List<PacketExtension> getExtensions();
}

View File

@ -0,0 +1,74 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Arrays;
import java.util.List;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
/**
* Represents the top level element of a pubsub event extension. All types of pubsub events are
* represented by this class. The specific type can be found by {@link #getEventType()}. The
* embedded event information, which is specific to the event type, can be retrieved by the {@link #getEvent()}
* method.
*
* @author Robin Collier
*/
public class EventElement implements EmbeddedPacketExtension
{
private EventElementType type;
private NodeExtension ext;
public EventElement(EventElementType eventType, NodeExtension eventExt)
{
type = eventType;
ext = eventExt;
}
public EventElementType getEventType()
{
return type;
}
public List<PacketExtension> getExtensions()
{
return Arrays.asList(new PacketExtension[]{getEvent()});
}
public NodeExtension getEvent()
{
return ext;
}
public String getElementName()
{
return "event";
}
public String getNamespace()
{
return PubSubNamespace.EVENT.getXmlns();
}
public String toXML()
{
StringBuilder builder = new StringBuilder("<event xmlns='" + PubSubNamespace.EVENT.getXmlns() + "'>");
builder.append(ext.toXML());
builder.append("</event>");
return builder.toString();
}
}

View File

@ -0,0 +1,41 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* This enumeration defines the possible event types that are supported within pubsub
* event messages.
*
* @author Robin Collier
*/
public enum EventElementType
{
/** A node has been associated or dissassociated with a collection node */
collection,
/** A node has had its configuration changed */
configuration,
/** A node has been deleted */
delete,
/** Items have been published to a node */
items,
/** All items have been purged from a node */
purge,
/** A node has been subscribed to */
subscription
}

View File

@ -0,0 +1,99 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smackx.Form;
/**
* Generic packet extension which represents any pubsub form that is
* parsed from the incoming stream or being sent out to the server.
*
* Form types are defined in {@link FormNodeType}.
*
* @author Robin Collier
*/
public class FormNode extends NodeExtension
{
private Form configForm;
/**
* Create a {@link FormNode} which contains the specified form.
*
* @param formType The type of form being sent
* @param submitForm The form
*/
public FormNode(FormNodeType formType, Form submitForm)
{
super(formType.getNodeElement());
if (submitForm == null)
throw new IllegalArgumentException("Submit form cannot be null");
configForm = submitForm;
}
/**
* Create a {@link FormNode} which contains the specified form, which is
* associated with the specified node.
*
* @param formType The type of form being sent
* @param nodeId The node the form is associated with
* @param submitForm The form
*/
public FormNode(FormNodeType formType, String nodeId, Form submitForm)
{
super(formType.getNodeElement(), nodeId);
if (submitForm == null)
throw new IllegalArgumentException("Submit form cannot be null");
configForm = submitForm;
}
/**
* Get the Form that is to be sent, or was retrieved from the server.
*
* @return The form
*/
public Form getForm()
{
return configForm;
}
@Override
public String toXML()
{
if (configForm == null)
{
return super.toXML();
}
else
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
if (getNode() != null)
{
builder.append(" node='");
builder.append(getNode());
builder.append("'>");
}
else
builder.append('>');
builder.append(configForm.getDataFormToSend().toXML());
builder.append("</");
builder.append(getElementName() + '>');
return builder.toString();
}
}
}

View File

@ -0,0 +1,50 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
/**
* The types of forms supported by the pubsub specification.
*
* @author Robin Collier
*/
public enum FormNodeType
{
/** Form for configuring an existing node */
CONFIGURE_OWNER,
/** Form for configuring a node during creation */
CONFIGURE,
/** Form for configuring subscription options */
OPTIONS,
/** Form which represents the default node configuration options */
DEFAULT;
public PubSubElementType getNodeElement()
{
return PubSubElementType.valueOf(toString());
}
public static FormNodeType valueOfFromElementName(String elem, String configNamespace)
{
if ("configure".equals(elem) && PubSubNamespace.OWNER.getXmlns().equals(configNamespace))
{
return CONFIGURE_OWNER;
}
return valueOf(elem.toUpperCase());
}
}

View File

@ -0,0 +1,26 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smackx.Form;
/**
* Defines the allowable types for a {@link Form}
*
* @author Robin Collier
*/
public enum FormType
{
form, submit, cancel, result;
}

View File

@ -0,0 +1,109 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
/**
* This class represents an item that has been, or will be published to a
* pubsub node. An <tt>Item</tt> has several properties that are dependent
* on the configuration of the node to which it has been or will be published.
*
* <h1>An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}</b>
* <li>Will always have an id (either user or server generated) unless node configuration has both
* {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false.
* <li>Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set
* to true, otherwise it will be null.
*
* <h1>An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}</b>
* <li>The id is optional, since the server will generate one if necessary, but should be used if it is
* meaningful in the context of the node. This value must be unique within the node that it is sent to, since
* resending an item with the same id will overwrite the one that already exists if the items are persisted.
* <li>Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set
* to true.
*
* <p>To customise the payload object being returned from the {@link #getPayload()} method, you can
* add a custom parser as explained in {@link ItemProvider}.
*
* @author Robin Collier
*/
public class Item implements PacketExtension
{
private String id;
/**
* Create an empty <tt>Item</tt> with no id. This is a valid item for nodes which are configured
* so that {@link ConfigureForm#isDeliverPayloads()} is false. In most cases an id will be generated by the server.
* For nodes configured with {@link ConfigureForm#isDeliverPayloads()} and {@link ConfigureForm#isPersistItems()}
* set to false, no <tt>Item</tt> is sent to the node, you have to use {@link LeafNode#send()} or {@link LeafNode#publish()}
* methods in this case.
*/
public Item()
{
}
/**
* Create an <tt>Item</tt> with an id but no payload. This is a valid item for nodes which are configured
* so that {@link ConfigureForm#isDeliverPayloads()} is false.
*
* @param itemId The id if the item. It must be unique within the node unless overwriting and existing item.
* Passing null is the equivalent of calling {@link #Item()}.
*/
public Item(String itemId)
{
id = itemId;
}
/**
* Get the item id. Unique to the node it is associated with.
*
* @return The id
*/
public String getId()
{
return id;
}
public String getElementName()
{
return "item";
}
public String getNamespace()
{
return null;
}
public String toXML()
{
StringBuilder builder = new StringBuilder("<item");
if (id != null)
{
builder.append(" id='");
builder.append(id);
builder.append("'");
}
builder.append("/>");
return builder.toString();
}
@Override
public String toString()
{
return getClass().getName() + " | Content [" + toXML() + "]";
}
}

View File

@ -0,0 +1,62 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Collections;
import java.util.List;
/**
* Represents an event in which items have been deleted from the node.
*
* @author Robin Collier
*/
public class ItemDeleteEvent extends SubscriptionEvent
{
private List<String> itemIds = Collections.EMPTY_LIST;
/**
* Constructs an <tt>ItemDeleteEvent</tt> that indicates the the supplied
* items (by id) have been deleted, and that the event matches the listed
* subscriptions. The subscriptions would have been created by calling
* {@link LeafNode#subscribe(String)}.
*
* @param nodeId The id of the node the event came from
* @param deletedItemIds The item ids of the items that were deleted.
* @param subscriptionIds The subscriptions that match the event.
*/
public ItemDeleteEvent(String nodeId, List<String> deletedItemIds, List<String> subscriptionIds)
{
super(nodeId, subscriptionIds);
if (deletedItemIds == null)
throw new IllegalArgumentException("deletedItemIds cannot be null");
itemIds = deletedItemIds;
}
/**
* Get the item id's of the items that have been deleted.
*
* @return List of item id's
*/
public List<String> getItemIds()
{
return Collections.unmodifiableList(itemIds);
}
@Override
public String toString()
{
return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Deleted Items: " + itemIds + ']';
}
}

View File

@ -0,0 +1,123 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Represents an event generated by an item(s) being published to a node.
*
* @author Robin Collier
*/
public class ItemPublishEvent <T extends Item> extends SubscriptionEvent
{
private List<T> items;
private Date originalDate;
/**
* Constructs an <tt>ItemPublishEvent</tt> with the provided list
* of {@link Item} that were published.
*
* @param nodeId The id of the node the event came from
* @param eventItems The list of {@link Item} that were published
*/
public ItemPublishEvent(String nodeId, List<T> eventItems)
{
super(nodeId);
items = eventItems;
}
/**
* Constructs an <tt>ItemPublishEvent</tt> with the provided list
* of {@link Item} that were published. The list of subscription ids
* represents the subscriptions that matched the event, in the case
* of the user having multiple subscriptions.
*
* @param nodeId The id of the node the event came from
* @param eventItems The list of {@link Item} that were published
* @param subscriptionIds The list of subscriptionIds
*/
public ItemPublishEvent(String nodeId, List<T> eventItems, List<String> subscriptionIds)
{
super(nodeId, subscriptionIds);
items = eventItems;
}
/**
* Constructs an <tt>ItemPublishEvent</tt> with the provided list
* of {@link Item} that were published in the past. The published
* date signifies that this is delayed event. The list of subscription ids
* represents the subscriptions that matched the event, in the case
* of the user having multiple subscriptions.
*
* @param nodeId The id of the node the event came from
* @param eventItems The list of {@link Item} that were published
* @param subscriptionIds The list of subscriptionIds
* @param publishedDate The original publishing date of the events
*/
public ItemPublishEvent(String nodeId, List<T> eventItems, List<String> subscriptionIds, Date publishedDate)
{
super(nodeId, subscriptionIds);
items = eventItems;
if (publishedDate != null)
originalDate = publishedDate;
}
/**
* Get the list of {@link Item} that were published.
*
* @return The list of published {@link Item}
*/
public List<T> getItems()
{
return Collections.unmodifiableList(items);
}
/**
* Indicates whether this event was delayed. That is, the items
* were published to the node at some time in the past. This will
* typically happen if there is an item already published to the node
* before a user subscribes to it. In this case, when the user
* subscribes, the server may send the last item published to the node
* with a delay date showing its time of original publication.
*
* @return true if the items are delayed, false otherwise.
*/
public boolean isDelayed()
{
return (originalDate != null);
}
/**
* Gets the original date the items were published. This is only
* valid if {@link #isDelayed()} is true.
*
* @return Date items were published if {@link #isDelayed()} is true, null otherwise.
*/
public Date getPublishedDate()
{
return originalDate;
}
@Override
public String toString()
{
return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Delayed: " +
(isDelayed() ? originalDate.toString() : "false") + ']';
}
}

View File

@ -0,0 +1,29 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* These are the options for the node configuration setting {@link ConfigureForm#setItemReply(ItemReply)},
* which defines who should receive replies to items.
*
* @author Robin Collier
*/
public enum ItemReply
{
/** The node owner */
owner,
/** The item publisher */
publisher;
}

View File

@ -0,0 +1,183 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.List;
import org.jivesoftware.smack.packet.PacketExtension;
/**
* This class is used to for multiple purposes.
* <li>It can represent an event containing a list of items that have been published
* <li>It can represent an event containing a list of retracted (deleted) items.
* <li>It can represent a request to delete a list of items.
* <li>It can represent a request to get existing items.
*
* <p><b>Please note, this class is used for internal purposes, and is not required for usage of
* pubsub functionality.</b>
*
* @author Robin Collier
*/
public class ItemsExtension extends NodeExtension implements EmbeddedPacketExtension
{
protected ItemsElementType type;
protected String attValue;
protected List<? extends PacketExtension> items;
public enum ItemsElementType
{
/** An items element, which has an optional <b>max_items</b> attribute when requesting items */
items(PubSubElementType.ITEMS, "max_items"),
/** A retract element, which has an optional <b>notify</b> attribute when publishing deletions */
retract(PubSubElementType.RETRACT, "notify");
private PubSubElementType elem;
private String att;
private ItemsElementType(PubSubElementType nodeElement, String attribute)
{
elem = nodeElement;
att = attribute;
}
public PubSubElementType getNodeElement()
{
return elem;
}
public String getElementAttribute()
{
return att;
}
}
/**
* Construct an instance with a list representing items that have been published or deleted.
*
* <p>Valid scenarios are:
* <li>Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an
* optional value for the <b>max_items</b> attribute.
* <li>Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing
* only id's and an optional value for the <b>notify</b> attribute.
* <li>Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and
* attributeValue = <code>null</code>
* <li>Items deleted event - itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and
* attributeValue = <code>null</code>
*
* @param itemsType Type of representation
* @param nodeId The node to which the items are being sent or deleted
* @param items The list of {@link Item} or {@link RetractItem}
* @param attributeValue The value of the <b>max_items</b>
*/
public ItemsExtension(ItemsElementType itemsType, String nodeId, List<? extends PacketExtension> items, String attributeValue)
{
super(itemsType.getNodeElement(), nodeId);
type = itemsType;
this.items = items;
attValue = attributeValue;
}
/**
* Constructs a request to get items from the node as defined in the first scenario
* in {@link #ItemsExtension(ItemsElementType, String, List, String)}
*
* @param nodeId The node the items will be requested from
* @param maxItems The limit on the number of items to retrieve (null for all)
*/
public ItemsExtension(String nodeId, Integer maxItems)
{
this(ItemsElementType.items, nodeId, null, maxItems == null ? null : maxItems.toString());
}
/**
* Get the type of element
*
* @return The element type
*/
public ItemsElementType getItemsElementType()
{
return type;
}
public List<PacketExtension> getExtensions()
{
return (List<PacketExtension>)getItems();
}
/**
* Gets the items related to the type of request or event.
*
* return List of {@link Item}, {@link RetractItem}, or null
*/
public List<? extends PacketExtension> getItems()
{
return items;
}
/**
* Gets the value of the optional attribute related to the {@link ItemsElementType}.
*
* @return The attribute value
*/
public String getAttributeValue()
{
return attValue;
}
@Override
public String toXML()
{
if (((items == null) || (items.size() == 0)) && (attValue == null))
{
return super.toXML();
}
else
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
builder.append(" node='");
builder.append(getNode());
if (attValue != null)
{
builder.append("' ");
builder.append(type.getElementAttribute());
builder.append("='");
builder.append(attValue);
builder.append("'>");
}
else
{
builder.append("'>");
for (PacketExtension item : items)
{
builder.append(item.toXML());
}
}
builder.append("</");
builder.append(getElementName());
builder.append(">");
return builder.toString();
}
}
@Override
public String toString()
{
return getClass().getName() + "Content [" + toXML() + "]";
}
}

View File

@ -0,0 +1,309 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smackx.packet.DiscoverItems;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
/**
* The main class for the majority of pubsub functionality. In general
* almost all pubsub capabilities are related to the concept of a node.
* All items are published to a node, and typically subscribed to by other
* users. These users then retrieve events based on this subscription.
*
* @author Robin Collier
*/
public class LeafNode extends Node
{
LeafNode(XMPPConnection connection, String nodeName)
{
super(connection, nodeName);
}
/**
* Get information on the items in the node in standard
* {@link DiscoverItems} format.
*
* @return The item details in {@link DiscoverItems} format
*
* @throws XMPPException
*/
public DiscoverItems discoverItems()
throws XMPPException
{
DiscoverItems items = new DiscoverItems();
items.setTo(to);
items.setNode(getId());
return (DiscoverItems)SyncPacketSend.getReply(con, items);
}
/**
* Get the current items stored in the node.
*
* @return List of {@link Item} in the node
*
* @throws XMPPException
*/
public <T extends Item> List<T> getItems()
throws XMPPException
{
PubSub request = createPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.ITEMS, getId()));
PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
return (List<T>)itemsElem.getItems();
}
/**
* Get the items specified from the node. This would typically be
* used when the server does not return the payload due to size
* constraints. The user would be required to retrieve the payload
* after the items have been retrieved via {@link #getItems()} or an
* event, that did not include the payload.
*
* @param ids Item ids of the items to retrieve
*
* @return The list of {@link Item} with payload
*
* @throws XMPPException
*/
public <T extends Item> List<T> getItems(Collection<String> ids)
throws XMPPException
{
List<Item> itemList = new ArrayList<Item>(ids.size());
for (String id : ids)
{
itemList.add(new Item(id));
}
PubSub request = createPubsubPacket(Type.GET, new ItemsExtension(ItemsExtension.ItemsElementType.items, getId(), itemList, null));
PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
return (List<T>)itemsElem.getItems();
}
/**
* Get items persisted on the node, limited to the specified number.
*
* @param maxItems Maximum number of items to return
*
* @return List of {@link Item}
*
* @throws XMPPException
*/
public <T extends Item> List<T> getItems(int maxItems)
throws XMPPException
{
PubSub request = createPubsubPacket(Type.GET, new ItemsExtension(getId(), Integer.valueOf(maxItems)));
PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
return (List<T>)itemsElem.getItems();
}
/**
* Publishes an event to the node. This is an empty event
* with no item.
*
* This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false
* and {@link ConfigureForm#isDeliverPayloads()}=false.
*
* This is an asynchronous call which returns as soon as the
* packet has been sent.
*
* For synchronous calls use {@link #send() send()}.
*/
public void publish()
{
PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));
con.sendPacket(packet);
}
/**
* Publishes an event to the node. This is a simple item
* with no payload.
*
* If the id is null, an empty item (one without an id) will be sent.
* Please note that this is not the same as {@link #send()}, which
* publishes an event with NO item.
*
* This is an asynchronous call which returns as soon as the
* packet has been sent.
*
* For synchronous calls use {@link #send(Item) send(Item))}.
*
* @param item - The item being sent
*/
public <T extends Item> void publish(T item)
{
Collection<T> items = new ArrayList<T>(1);
items.add((T)(item == null ? new Item() : item));
publish(items);
}
/**
* Publishes multiple events to the node. Same rules apply as in {@link #publish(Item)}.
*
* In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input
* list will get stored on the node, assuming it stores the last sent item.
*
* This is an asynchronous call which returns as soon as the
* packet has been sent.
*
* For synchronous calls use {@link #send(Collection) send(Collection))}.
*
* @param items - The collection of items being sent
*/
public <T extends Item> void publish(Collection<T> items)
{
PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));
con.sendPacket(packet);
}
/**
* Publishes an event to the node. This is an empty event
* with no item.
*
* This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false
* and {@link ConfigureForm#isDeliverPayloads()}=false.
*
* This is a synchronous call which will throw an exception
* on failure.
*
* For asynchronous calls, use {@link #publish() publish()}.
*
* @throws XMPPException
*/
public void send()
throws XMPPException
{
PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));
SyncPacketSend.getReply(con, packet);
}
/**
* Publishes an event to the node. This can be either a simple item
* with no payload, or one with it. This is determined by the Node
* configuration.
*
* If the node has <b>deliver_payload=false</b>, the Item must not
* have a payload.
*
* If the id is null, an empty item (one without an id) will be sent.
* Please note that this is not the same as {@link #send()}, which
* publishes an event with NO item.
*
* This is a synchronous call which will throw an exception
* on failure.
*
* For asynchronous calls, use {@link #publish(Item) publish(Item)}.
*
* @param item - The item being sent
*
* @throws XMPPException
*/
public <T extends Item> void send(T item)
throws XMPPException
{
Collection<T> items = new ArrayList<T>(1);
items.add((item == null ? (T)new Item() : item));
send(items);
}
/**
* Publishes multiple events to the node. Same rules apply as in {@link #send(Item)}.
*
* In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input
* list will get stored on the node, assuming it stores the last sent item.
*
* This is a synchronous call which will throw an exception
* on failure.
*
* For asynchronous calls, use {@link #publish(Collection) publish(Collection))}.
*
* @param items - The collection of {@link Item} objects being sent
*
* @throws XMPPException
*/
public <T extends Item> void send(Collection<T> items)
throws XMPPException
{
PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));
SyncPacketSend.getReply(con, packet);
}
/**
* Purges the node of all items.
*
* <p>Note: Some implementations may keep the last item
* sent.
*
* @throws XMPPException
*/
public void deleteAllItems()
throws XMPPException
{
PubSub request = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PURGE_OWNER, getId()), PubSubElementType.PURGE_OWNER.getNamespace());
SyncPacketSend.getReply(con, request);
}
/**
* Delete the item with the specified id from the node.
*
* @param itemId The id of the item
*
* @throws XMPPException
*/
public void deleteItem(String itemId)
throws XMPPException
{
Collection<String> items = new ArrayList<String>(1);
items.add(itemId);
deleteItem(items);
}
/**
* Delete the items with the specified id's from the node.
*
* @param itemIds The list of id's of items to delete
*
* @throws XMPPException
*/
public void deleteItem(Collection<String> itemIds)
throws XMPPException
{
List<Item> items = new ArrayList<Item>(itemIds.size());
for (String id : itemIds)
{
items.add(new Item(id));
}
PubSub request = createPubsubPacket(Type.SET, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items, null));
SyncPacketSend.getReply(con, request);
}
}

View File

@ -0,0 +1,525 @@
/*
* Created on 2009-07-09
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.OrFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.jivesoftware.smackx.packet.DiscoverInfo;
import org.jivesoftware.smackx.packet.Header;
import org.jivesoftware.smackx.packet.HeadersExtension;
import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;
import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
abstract public class Node
{
protected XMPPConnection con;
protected String id;
protected String to;
protected ConcurrentHashMap<ItemEventListener, PacketListener> itemEventToListenerMap = new ConcurrentHashMap<ItemEventListener, PacketListener>();
protected ConcurrentHashMap<ItemDeleteListener, PacketListener> itemDeleteToListenerMap = new ConcurrentHashMap<ItemDeleteListener, PacketListener>();
protected ConcurrentHashMap<NodeConfigListener, PacketListener> configEventToListenerMap = new ConcurrentHashMap<NodeConfigListener, PacketListener>();
/**
* Construct a node associated to the supplied connection with the specified
* node id.
*
* @param connection The connection the node is associated with
* @param nodeName The node id
*/
Node(XMPPConnection connection, String nodeName)
{
con = connection;
id = nodeName;
}
/**
* Some XMPP servers may require a specific service to be addressed on the
* server.
*
* For example, OpenFire requires the server to be prefixed by <b>pubsub</b>
*/
void setTo(String toAddress)
{
to = toAddress;
}
/**
* Get the NodeId
*
* @return the node id
*/
public String getId()
{
return id;
}
/**
* Returns a configuration form, from which you can create an answer form to be submitted
* via the {@link #sendConfigurationForm(Form)}.
*
* @return the configuration form
*/
public ConfigureForm getNodeConfiguration()
throws XMPPException
{
Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.CONFIGURE_OWNER, getId()), PubSubNamespace.OWNER);
return NodeUtils.getFormFromPacket(reply, PubSubElementType.CONFIGURE_OWNER);
}
/**
* Update the configuration with the contents of the new {@link Form}
*
* @param submitForm
*/
public void sendConfigurationForm(Form submitForm)
throws XMPPException
{
PubSub packet = createPubsubPacket(Type.SET, new FormNode(FormNodeType.CONFIGURE_OWNER, getId(), submitForm), PubSubNamespace.OWNER);
SyncPacketSend.getReply(con, packet);
}
/**
* Discover node information in standard {@link DiscoverInfo} format.
*
* @return The discovery information about the node.
*
* @throws XMPPException
*/
public DiscoverInfo discoverInfo()
throws XMPPException
{
DiscoverInfo info = new DiscoverInfo();
info.setTo(to);
info.setNode(getId());
return (DiscoverInfo)SyncPacketSend.getReply(con, info);
}
/**
* Get the subscriptions currently associated with this node.
*
* @return List of {@link Subscription}
*
* @throws XMPPException
*/
public List<Subscription> getSubscriptions()
throws XMPPException
{
PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS, getId()));
SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS);
return subElem.getSubscriptions();
}
/**
* The user subscribes to the node using the supplied jid. The
* bare jid portion of this one must match the jid for the connection.
*
* Please note that the {@link Subscription.State} should be checked
* on return since more actions may be required by the caller.
* {@link Subscription.State#pending} - The owner must approve the subscription
* request before messages will be received.
* {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true,
* the caller must configure the subscription before messages will be received. If it is false
* the caller can configure it but is not required to do so.
* @param jid The jid to subscribe as.
* @return The subscription
* @exception XMPPException
*/
public Subscription subscribe(String jid)
throws XMPPException
{
PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));
return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);
}
/**
* The user subscribes to the node using the supplied jid and subscription
* options. The bare jid portion of this one must match the jid for the
* connection.
*
* Please note that the {@link Subscription.State} should be checked
* on return since more actions may be required by the caller.
* {@link Subscription.State#pending} - The owner must approve the subscription
* request before messages will be received.
* {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true,
* the caller must configure the subscription before messages will be received. If it is false
* the caller can configure it but is not required to do so.
* @param jid The jid to subscribe as.
* @return The subscription
* @exception XMPPException
*/
public Subscription subscribe(String jid, SubscribeForm subForm)
throws XMPPException
{
PubSub request = createPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));
request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
PubSub reply = (PubSub)PubSubManager.sendPubsubPacket(con, jid, Type.SET, request);
return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);
}
/**
* Remove the subscription related to the specified JID. This will only
* work if there is only 1 subscription. If there are multiple subscriptions,
* use {@link #unsubscribe(String, String)}.
*
* @param jid The JID used to subscribe to the node
*
* @throws XMPPException
*/
public void unsubscribe(String jid)
throws XMPPException
{
unsubscribe(jid, null);
}
/**
* Remove the specific subscription related to the specified JID.
*
* @param jid The JID used to subscribe to the node
* @param subscriptionId The id of the subscription being removed
*
* @throws XMPPException
*/
public void unsubscribe(String jid, String subscriptionId)
throws XMPPException
{
sendPubsubPacket(Type.SET, new UnsubscribeExtension(jid, getId(), subscriptionId));
}
/**
* Returns a SubscribeForm for subscriptions, from which you can create an answer form to be submitted
* via the {@link #sendConfigurationForm(Form)}.
*
* @return A subscription options form
*
* @throws XMPPException
*/
public SubscribeForm getSubscriptionOptions(String jid)
throws XMPPException
{
return getSubscriptionOptions(jid, null);
}
/**
* Get the options for configuring the specified subscription.
*
* @param jid JID the subscription is registered under
* @param subscriptionId The subscription id
*
* @return The subscription option form
*
* @throws XMPPException
*/
public SubscribeForm getSubscriptionOptions(String jid, String subscriptionId)
throws XMPPException
{
PubSub packet = (PubSub)sendPubsubPacket(Type.GET, new OptionsExtension(jid, getId(), subscriptionId));
FormNode ext = (FormNode)packet.getExtension(PubSubElementType.OPTIONS);
return new SubscribeForm(ext.getForm());
}
/**
* Register a listener for item publication events. This
* listener will get called whenever an item is published to
* this node.
*
* @param listener The handler for the event
*/
public void addItemEventListener(ItemEventListener listener)
{
PacketListener conListener = new ItemEventTranslator(listener);
itemEventToListenerMap.put(listener, conListener);
con.addPacketListener(conListener, new EventContentFilter(EventElementType.items.toString(), "item"));
}
/**
* Unregister a listener for publication events.
*
* @param listener The handler to unregister
*/
public void removeItemEventListener(ItemEventListener listener)
{
PacketListener conListener = itemEventToListenerMap.remove(listener);
if (conListener != null)
con.removePacketListener(conListener);
}
/**
* Register a listener for configuration events. This listener
* will get called whenever the node's configuration changes.
*
* @param listener The handler for the event
*/
public void addConfigurationListener(NodeConfigListener listener)
{
PacketListener conListener = new NodeConfigTranslator(listener);
configEventToListenerMap.put(listener, conListener);
con.addPacketListener(conListener, new EventContentFilter(EventElementType.configuration.toString()));
}
/**
* Unregister a listener for configuration events.
*
* @param listener The handler to unregister
*/
public void removeConfigurationListener(NodeConfigListener listener)
{
PacketListener conListener = configEventToListenerMap .remove(listener);
if (conListener != null)
con.removePacketListener(conListener);
}
/**
* Register an listener for item delete events. This listener
* gets called whenever an item is deleted from the node.
*
* @param listener The handler for the event
*/
public void addItemDeleteListener(ItemDeleteListener listener)
{
PacketListener delListener = new ItemDeleteTranslator(listener);
itemDeleteToListenerMap.put(listener, delListener);
EventContentFilter deleteItem = new EventContentFilter(EventElementType.items.toString(), "retract");
EventContentFilter purge = new EventContentFilter(EventElementType.purge.toString());
con.addPacketListener(delListener, new OrFilter(deleteItem, purge));
}
/**
* Unregister a listener for item delete events.
*
* @param listener The handler to unregister
*/
public void removeItemDeleteListener(ItemDeleteListener listener)
{
PacketListener conListener = itemDeleteToListenerMap .remove(listener);
if (conListener != null)
con.removePacketListener(conListener);
}
@Override
public String toString()
{
return super.toString() + " " + getClass().getName() + " id: " + id;
}
protected PubSub createPubsubPacket(Type type, PacketExtension ext)
{
return createPubsubPacket(type, ext, null);
}
protected PubSub createPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)
{
return PubSubManager.createPubsubPacket(to, type, ext, ns);
}
protected Packet sendPubsubPacket(Type type, NodeExtension ext)
throws XMPPException
{
return PubSubManager.sendPubsubPacket(con, to, type, ext);
}
protected Packet sendPubsubPacket(Type type, NodeExtension ext, PubSubNamespace ns)
throws XMPPException
{
return PubSubManager.sendPubsubPacket(con, to, type, ext, ns);
}
private static List<String> getSubscriptionIds(Packet packet)
{
HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim");
List<String> values = null;
if (headers != null)
{
values = new ArrayList<String>(headers.getHeaders().size());
for (Header header : headers.getHeaders())
{
values.add(header.getValue());
}
}
return values;
}
/**
* This class translates low level item publication events into api level objects for
* user consumption.
*
* @author Robin Collier
*/
public class ItemEventTranslator implements PacketListener
{
private ItemEventListener listener;
public ItemEventTranslator(ItemEventListener eventListener)
{
listener = eventListener;
}
public void processPacket(Packet packet)
{
EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
DelayInformation delay = (DelayInformation)packet.getExtension("delay", "urn:xmpp:delay");
// If there was no delay based on XEP-0203, then try XEP-0091 for backward compatibility
if (delay == null)
{
delay = (DelayInformation)packet.getExtension("x", "jabber:x:delay");
}
ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), (List<Item>)itemsElem.getItems(), getSubscriptionIds(packet), (delay == null ? null : delay.getStamp()));
listener.handlePublishedItems(eventItems);
}
}
/**
* This class translates low level item deletion events into api level objects for
* user consumption.
*
* @author Robin Collier
*/
public class ItemDeleteTranslator implements PacketListener
{
private ItemDeleteListener listener;
public ItemDeleteTranslator(ItemDeleteListener eventListener)
{
listener = eventListener;
}
public void processPacket(Packet packet)
{
EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
List<PacketExtension> extList = event.getExtensions();
if (extList.get(0).getElementName().equals(PubSubElementType.PURGE_EVENT.getElementName()))
{
listener.handlePurge();
}
else
{
ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
Collection<? extends PacketExtension> pubItems = itemsElem.getItems();
Iterator<RetractItem> it = (Iterator<RetractItem>)pubItems.iterator();
List<String> items = new ArrayList<String>(pubItems.size());
while (it.hasNext())
{
RetractItem item = it.next();
items.add(item.getId());
}
ItemDeleteEvent eventItems = new ItemDeleteEvent(itemsElem.getNode(), items, getSubscriptionIds(packet));
listener.handleDeletedItems(eventItems);
}
}
}
/**
* This class translates low level node configuration events into api level objects for
* user consumption.
*
* @author Robin Collier
*/
public class NodeConfigTranslator implements PacketListener
{
private NodeConfigListener listener;
public NodeConfigTranslator(NodeConfigListener eventListener)
{
listener = eventListener;
}
public void processPacket(Packet packet)
{
EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
ConfigurationEvent config = (ConfigurationEvent)event.getEvent();
listener.handleNodeConfiguration(config);
}
}
/**
* Filter for {@link PacketListener} to filter out events not specific to the
* event type expected for this node.
*
* @author Robin Collier
*/
class EventContentFilter implements PacketFilter
{
private String firstElement;
private String secondElement;
EventContentFilter(String elementName)
{
firstElement = elementName;
}
EventContentFilter(String firstLevelEelement, String secondLevelElement)
{
firstElement = firstLevelEelement;
secondElement = secondLevelElement;
}
public boolean accept(Packet packet)
{
if (!(packet instanceof Message))
return false;
EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
if (event == null)
return false;
NodeExtension embedEvent = event.getEvent();
if (embedEvent == null)
return false;
if (embedEvent.getElementName().equals(firstElement))
{
if (!embedEvent.getNode().equals(getId()))
return false;
if (secondElement == null)
return true;
if (embedEvent instanceof EmbeddedPacketExtension)
{
List<PacketExtension> secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions();
if (secondLevelList.size() > 0 && secondLevelList.get(0).getElementName().equals(secondElement))
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,19 @@
/*
* Created on 2009-05-12
*/
package org.jivesoftware.smackx.pubsub;
abstract public class NodeEvent
{
private String nodeId;
protected NodeEvent(String id)
{
nodeId = id;
}
public String getNodeId()
{
return nodeId;
}
}

View File

@ -0,0 +1,85 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
/**
* A class which represents a common element within the pubsub defined
* schemas. One which has a <b>node</b> as an attribute. This class is
* used on its own as well as a base class for many others, since the
* node is a central concept to most pubsub functionality.
*
* @author Robin Collier
*/
public class NodeExtension implements PacketExtension
{
private PubSubElementType element;
private String node;
/**
* Constructs a <tt>NodeExtension</tt> with an element name specified
* by {@link PubSubElementType} and the specified node id.
*
* @param elem Defines the element name and namespace
* @param nodeId Specifies the id of the node
*/
public NodeExtension(PubSubElementType elem, String nodeId)
{
element = elem;
this.node = nodeId;
}
/**
* Constructs a <tt>NodeExtension</tt> with an element name specified
* by {@link PubSubElementType}.
*
* @param elem Defines the element name and namespace
*/
public NodeExtension(PubSubElementType elem)
{
this(elem, null);
}
/**
* Gets the node id
*
* @return The node id
*/
public String getNode()
{
return node;
}
public String getElementName()
{
return element.getElementName();
}
public String getNamespace()
{
return element.getNamespace().getXmlns();
}
public String toXML()
{
return '<' + getElementName() + (node == null ? "" : " node='" + node + '\'') + "/>";
}
@Override
public String toString()
{
return getClass().getName() + " - content [" + toXML() + "]";
}
}

View File

@ -0,0 +1,25 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* Defines the available types of nodes
*
* @author Robin Collier
*/
public enum NodeType
{
leaf,
collection;
}

View File

@ -0,0 +1,72 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smackx.pubsub.util.XmlUtils;
/**
* A packet extension representing the <b>options</b> element.
*
* @author Robin Collier
*/
public class OptionsExtension extends NodeExtension
{
protected String jid;
protected String id;
public OptionsExtension(String subscriptionJid)
{
this(subscriptionJid, null, null);
}
public OptionsExtension(String subscriptionJid, String nodeId)
{
this(subscriptionJid, nodeId, null);
}
public OptionsExtension(String jid, String nodeId, String subscriptionId)
{
super(PubSubElementType.OPTIONS, nodeId);
this.jid = jid;
id = subscriptionId;
}
public String getJid()
{
return jid;
}
public String getId()
{
return id;
}
@Override
public String toXML()
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
XmlUtils.appendAttribute(builder, "jid", jid);
if (getNode() != null)
XmlUtils.appendAttribute(builder, "node", getNode());
if (id != null)
XmlUtils.appendAttribute(builder, "subid", id);
builder.append("/>");
return builder.toString();
}
}

View File

@ -0,0 +1,95 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
/**
* This class represents an item that has been, or will be published to a
* pubsub node. An <tt>Item</tt> has several properties that are dependent
* on the configuration of the node to which it has been or will be published.
*
* <h1>An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}</b>
* <li>Will always have an id (either user or server generated) unless node configuration has both
* {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false.
* <li>Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set
* to true, otherwise it will be null.
*
* <h1>An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}</b>
* <li>The id is optional, since the server will generate one if necessary, but should be used if it is
* meaningful in the context of the node. This value must be unique within the node that it is sent to, since
* resending an item with the same id will overwrite the one that already exists if the items are persisted.
* <li>Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set
* to true.
*
* <p>To customise the payload object being returned from the {@link #getPayload()} method, you can
* add a custom parser as explained in {@link ItemProvider}.
*
* @author Robin Collier
*/
public class PayloadItem<E extends PacketExtension> extends Item
{
private E payload;
/**
* Create an <tt>Item</tt> with an id and payload.
*
* @param itemId The id of this item. It can be null if we want the server to set the id.
* @param payloadExt A {@link PacketExtension} which represents the payload data.
*/
public PayloadItem(String itemId, E payloadExt)
{
super(itemId);
if (payloadExt == null)
throw new IllegalArgumentException("payload cannot be 'null'");
payload = payloadExt;
}
/**
* Get the payload associated with this <tt>Item</tt>. Customising the payload
* parsing from the server can be accomplished as described in {@link ItemProvider}.
*
* @return The payload as a {@link PacketExtension}.
*/
public E getPayload()
{
return payload;
}
public String toXML()
{
StringBuilder builder = new StringBuilder("<item");
if (getId() != null)
{
builder.append(" id='");
builder.append(getId());
builder.append("'");
}
builder.append(">");
builder.append(payload.toXML());
builder.append("</item>");
return builder.toString();
}
@Override
public String toString()
{
return getClass().getName() + " | Content [" + toXML() + "]";
}
}

View File

@ -0,0 +1,25 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* Defines the possible valid presence states for node subscription via
* {@link SubscribeForm#getShowValues()}.
*
* @author Robin Collier
*/
public enum PresenceState
{
chat, online, away, xa, dnd
}

View File

@ -0,0 +1,77 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
/**
* Defines all the possible element types as defined for all the pubsub
* schemas in all 3 namespaces.
*
* @author Robin Collier
*/
public enum PubSubElementType
{
CREATE("create", PubSubNamespace.BASIC),
DELETE("delete", PubSubNamespace.OWNER),
DELETE_EVENT("delete", PubSubNamespace.EVENT),
CONFIGURE("configure", PubSubNamespace.BASIC),
CONFIGURE_OWNER("configure", PubSubNamespace.OWNER),
CONFIGURATION("configuration", PubSubNamespace.EVENT),
OPTIONS("options", PubSubNamespace.BASIC),
DEFAULT("default", PubSubNamespace.OWNER),
ITEMS("items", PubSubNamespace.BASIC),
PUBLISH("publish", PubSubNamespace.BASIC),
PUBLISH_OPTIONS("publish-options", PubSubNamespace.BASIC),
PURGE_OWNER("purge", PubSubNamespace.OWNER),
PURGE_EVENT("purge", PubSubNamespace.EVENT),
RETRACT("retract", PubSubNamespace.BASIC),
AFFILIATIONS("affiliations", PubSubNamespace.BASIC),
SUBSCRIBE("subscribe", PubSubNamespace.BASIC),
SUBSCRIPTION("subscription", PubSubNamespace.BASIC),
SUBSCRIPTIONS("subscriptions", PubSubNamespace.BASIC),
UNSUBSCRIBE("unsubscribe", PubSubNamespace.BASIC);
private String eName;
private PubSubNamespace nSpace;
private PubSubElementType(String elemName, PubSubNamespace ns)
{
eName = elemName;
nSpace = ns;
}
public PubSubNamespace getNamespace()
{
return nSpace;
}
public String getElementName()
{
return eName;
}
public static PubSubElementType valueOfFromElemName(String elemName, String namespace)
{
int index = namespace.lastIndexOf('#');
String fragment = (index == -1 ? null : namespace.substring(index+1));
if (fragment != null)
{
return valueOf((elemName + '_' + fragment).toUpperCase());
}
return valueOf(elemName.toUpperCase().replace('-', '_'));
}
}

View File

@ -0,0 +1,327 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.FormField;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.packet.DiscoverInfo;
import org.jivesoftware.smackx.packet.DiscoverItems;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
/**
* This is the starting point for access to the pubsub service. It
* will provide access to general information about the service, as
* well as create or retrieve pubsub {@link LeafNode} instances. These
* instances provide the bulk of the functionality as defined in the
* pubsub specification <a href="http://xmpp.org/extensions/xep-0060.html">XEP-0060</a>.
*
* @author Robin Collier
*/
final public class PubSubManager
{
private XMPPConnection con;
private String to;
private Map<String, Node> nodeMap = new ConcurrentHashMap<String, Node>();
/**
* Create a pubsub manager associated to the specified connection.
*
* @param connection The XMPP connection
*/
public PubSubManager(XMPPConnection connection)
{
con = connection;
}
/**
* Create a pubsub manager associated to the specified connection where
* the pubsub requests require a specific to address for packets.
*
* @param connection The XMPP connection
* @param toAddress The pubsub specific to address (required for some servers)
*/
public PubSubManager(XMPPConnection connection, String toAddress)
{
con = connection;
to = toAddress;
}
/**
* Creates an instant node, if supported.
*
* @return The node that was created
* @exception XMPPException
*/
public LeafNode createNode()
throws XMPPException
{
PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.CREATE));
NodeExtension elem = (NodeExtension)reply.getExtension("create", PubSubNamespace.BASIC.getXmlns());
LeafNode newNode = new LeafNode(con, elem.getNode());
newNode.setTo(to);
nodeMap.put(newNode.getId(), newNode);
return newNode;
}
/**
* Creates a node with default configuration.
*
* @param id The id of the node, which must be unique within the
* pubsub service
* @return The node that was created
* @exception XMPPException
*/
public LeafNode createNode(String id)
throws XMPPException
{
return (LeafNode)createNode(id, null);
}
/**
* Creates a node with specified configuration.
*
* Note: This is the only way to create a collection node.
*
* @param name The name of the node, which must be unique within the
* pubsub service
* @param config The configuration for the node
* @return The node that was created
* @exception XMPPException
*/
public Node createNode(String name, Form config)
throws XMPPException
{
PubSub request = createPubsubPacket(to, Type.SET, new NodeExtension(PubSubElementType.CREATE, name));
boolean isLeafNode = true;
if (config != null)
{
request.addExtension(new FormNode(FormNodeType.CONFIGURE, config));
FormField nodeTypeField = config.getField(ConfigureNodeFields.node_type.getFieldName());
if (nodeTypeField != null)
isLeafNode = nodeTypeField.getValues().next().equals(NodeType.leaf.toString());
}
// Errors will cause exceptions in getReply, so it only returns
// on success.
sendPubsubPacket(con, to, Type.SET, request);
Node newNode = isLeafNode ? new LeafNode(con, name) : new CollectionNode(con, name);
newNode.setTo(to);
nodeMap.put(newNode.getId(), newNode);
return newNode;
}
/**
* Retrieves the requested node, if it exists. It will throw an
* exception if it does not.
*
* @param id - The unique id of the node
* @return the node
* @throws XMPPException The node does not exist
*/
public Node getNode(String id)
throws XMPPException
{
Node node = nodeMap.get(id);
if (node == null)
{
DiscoverInfo info = new DiscoverInfo();
info.setTo(to);
info.setNode(id);
DiscoverInfo infoReply = (DiscoverInfo)SyncPacketSend.getReply(con, info);
if (infoReply.getIdentities().next().getType().equals(NodeType.leaf.toString()))
node = new LeafNode(con, id);
else
node = new CollectionNode(con, id);
node.setTo(to);
nodeMap.put(id, node);
}
return node;
}
/**
* Get all the nodes that currently exist as a child of the specified
* collection node. If the service does not support collection nodes
* then all nodes will be returned.
*
* To retrieve contents of the root collection node (if it exists),
* or there is no root collection node, pass null as the nodeId.
*
* @param nodeId - The id of the collection node for which the child
* nodes will be returned.
* @return {@link DiscoverItems} representing the existing nodes
*
* @throws XMPPException
*/
public DiscoverItems discoverNodes(String nodeId)
throws XMPPException
{
DiscoverItems items = new DiscoverItems();
if (nodeId != null)
items.setNode(nodeId);
items.setTo(to);
DiscoverItems nodeItems = (DiscoverItems)SyncPacketSend.getReply(con, items);
return nodeItems;
}
/**
* Gets the subscriptions on the root node.
*
* @return List of exceptions
*
* @throws XMPPException
*/
public List<Subscription> getSubscriptions()
throws XMPPException
{
Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS));
SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS.getElementName(), PubSubElementType.SUBSCRIPTIONS.getNamespace().getXmlns());
return subElem.getSubscriptions();
}
/**
* Gets the affiliations on the root node.
*
* @return List of affiliations
*
* @throws XMPPException
*/
public List<Affiliation> getAffiliations()
throws XMPPException
{
PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.AFFILIATIONS));
AffiliationsExtension listElem = (AffiliationsExtension)reply.getExtension(PubSubElementType.AFFILIATIONS);
return listElem.getAffiliations();
}
/**
* Delete the specified node
*
* @param nodeId
* @throws XMPPException
*/
public void deleteNode(String nodeId)
throws XMPPException
{
sendPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.DELETE, nodeId), PubSubElementType.DELETE.getNamespace());
nodeMap.remove(nodeId);
}
/**
* Returns the default settings for Node configuration.
*
* @return configuration form containing the default settings.
*/
public ConfigureForm getDefaultConfiguration()
throws XMPPException
{
// Errors will cause exceptions in getReply, so it only returns
// on success.
PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.DEFAULT), PubSubElementType.DEFAULT.getNamespace());
return NodeUtils.getFormFromPacket(reply, PubSubElementType.DEFAULT);
}
/**
* Gets the supported features of the servers pubsub implementation
* as a standard {@link DiscoverInfo} instance.
*
* @return The supported features
*
* @throws XMPPException
*/
public DiscoverInfo getSupportedFeatures()
throws XMPPException
{
ServiceDiscoveryManager mgr = ServiceDiscoveryManager.getInstanceFor(con);
return mgr.discoverInfo(to);
}
private Packet sendPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)
throws XMPPException
{
return sendPubsubPacket(con, to, type, ext, ns);
}
private Packet sendPubsubPacket(Type type, PacketExtension ext)
throws XMPPException
{
return sendPubsubPacket(type, ext, null);
}
static PubSub createPubsubPacket(String to, Type type, PacketExtension ext)
{
return createPubsubPacket(to, type, ext, null);
}
static PubSub createPubsubPacket(String to, Type type, PacketExtension ext, PubSubNamespace ns)
{
PubSub request = new PubSub();
request.setTo(to);
request.setType(type);
if (ns != null)
{
request.setPubSubNamespace(ns);
}
request.addExtension(ext);
return request;
}
static Packet sendPubsubPacket(XMPPConnection con, String to, Type type, PacketExtension ext)
throws XMPPException
{
return sendPubsubPacket(con, to, type, ext, null);
}
static Packet sendPubsubPacket(XMPPConnection con, String to, Type type, PacketExtension ext, PubSubNamespace ns)
throws XMPPException
{
return SyncPacketSend.getReply(con, createPubsubPacket(to, type, ext, ns));
}
static Packet sendPubsubPacket(XMPPConnection con, String to, Type type, PubSub packet)
throws XMPPException
{
return sendPubsubPacket(con, to, type, packet, null);
}
static Packet sendPubsubPacket(XMPPConnection con, String to, Type type, PubSub packet, PubSubNamespace ns)
throws XMPPException
{
return SyncPacketSend.getReply(con, packet);
}
}

View File

@ -0,0 +1,70 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Collection;
/**
* Represents a request to publish an item(s) to a specific node.
*
* @author Robin Collier
*/
public class PublishItem <T extends Item> extends NodeExtension
{
protected Collection<T> items;
/**
* Construct a request to publish an item to a node.
*
* @param nodeId The node to publish to
* @param toPublish The {@link Item} to publish
*/
public PublishItem(String nodeId, T toPublish)
{
super(PubSubElementType.PUBLISH, nodeId);
items = new ArrayList<T>(1);
items.add(toPublish);
}
/**
* Construct a request to publish multiple items to a node.
*
* @param nodeId The node to publish to
* @param toPublish The list of {@link Item} to publish
*/
public PublishItem(String nodeId, Collection<T> toPublish)
{
super(PubSubElementType.PUBLISH, nodeId);
items = toPublish;
}
@Override
public String toXML()
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
builder.append(" node='");
builder.append(getNode());
builder.append("'>");
for (Item item : items)
{
builder.append(item.toXML());
}
builder.append("</publish>");
return builder.toString();
}
}

View File

@ -0,0 +1,32 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* Determines who may publish to a node. Denotes possible values
* for {@link ConfigureForm#setPublishModel(PublishModel)}.
*
* @author Robin Collier
*/
public enum PublishModel
{
/** Only publishers may publish */
publishers,
/** Only subscribers may publish */
subscribers,
/** Anyone may publish */
open;
}

View File

@ -0,0 +1,59 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
/**
* Represents and item that has been deleted from a node.
*
* @author Robin Collier
*/
public class RetractItem implements PacketExtension
{
private String id;
/**
* Construct a <tt>RetractItem</tt> with the specified id.
*
* @param itemId The id if the item deleted
*/
public RetractItem(String itemId)
{
if (itemId == null)
throw new IllegalArgumentException("itemId must not be 'null'");
id = itemId;
}
public String getId()
{
return id;
}
public String getElementName()
{
return "retract";
}
public String getNamespace()
{
return PubSubNamespace.EVENT.getXmlns();
}
public String toXML()
{
return "<retract id='" + id + "'/>";
}
}

View File

@ -0,0 +1,65 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
/**
* The default payload representation for {@link Item#getPayload()}. It simply
* stores the XML payload as a string.
*
* @author Robin Collier
*/
public class SimplePayload implements PacketExtension
{
private String elemName;
private String ns;
private String payload;
/**
* Construct a <tt>SimplePayload</tt> object with the specified element name,
* namespace and content. The content must be well formed XML.
*
* @param elementName The root element name (of the payload)
* @param namespace The namespace of the payload, null if there is none
* @param xmlPayload The payload data
*/
public SimplePayload(String elementName, String namespace, String xmlPayload)
{
elemName = elementName;
payload = xmlPayload;
ns = namespace;
}
public String getElementName()
{
return elemName;
}
public String getNamespace()
{
return ns;
}
public String toXML()
{
return payload;
}
@Override
public String toString()
{
return getClass().getName() + "payload [" + toXML() + "]";
}
}

View File

@ -0,0 +1,60 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* Represents a request to subscribe to a node.
*
* @author Robin Collier
*/
public class SubscribeExtension extends NodeExtension
{
protected String jid;
public SubscribeExtension(String subscribeJid)
{
super(PubSubElementType.SUBSCRIBE);
jid = subscribeJid;
}
public SubscribeExtension(String subscribeJid, String nodeId)
{
super(PubSubElementType.SUBSCRIBE, nodeId);
jid = subscribeJid;
}
public String getJid()
{
return jid;
}
@Override
public String toXML()
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
if (getNode() != null)
{
builder.append(" node='");
builder.append(getNode());
builder.append("'");
}
builder.append(" jid='");
builder.append(getJid());
builder.append("'/>");
return builder.toString();
}
}

View File

@ -0,0 +1,242 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.UnknownFormatConversionException;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.FormField;
import org.jivesoftware.smackx.packet.DataForm;
/**
* A decorator for a {@link Form} to easily enable reading and updating
* of subscription options. All operations read or update the underlying {@link DataForm}.
*
* <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
* exist, all <b>SubscribeForm.setXXX</b> methods will create the field in the wrapped form
* if it does not already exist.
*
* @author Robin Collier
*/
public class SubscribeForm extends Form
{
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public SubscribeForm(DataForm configDataForm)
{
super(configDataForm);
}
public SubscribeForm(Form subscribeOptionsForm)
{
super(subscribeOptionsForm.getDataFormToSend());
}
public SubscribeForm(FormType formType)
{
super(formType.toString());
}
/**
* Determines if an entity wants to receive notifications.
*
* @return true if want to receive, false otherwise
*/
public boolean isDeliverOn()
{
return parseBoolean(getFieldValue(SubscribeOptionFields.deliver));
}
/**
* Sets whether an entity wants to receive notifications.
*
* @param deliverNotifications
*/
public void setDeliverOn(boolean deliverNotifications)
{
addField(SubscribeOptionFields.deliver, FormField.TYPE_BOOLEAN);
setAnswer(SubscribeOptionFields.deliver.getFieldName(), deliverNotifications);
}
/**
* Determines if notifications should be delivered as aggregations or not.
*
* @return true to aggregate, false otherwise
*/
public boolean isDigestOn()
{
return parseBoolean(getFieldValue(SubscribeOptionFields.digest));
}
/**
* Sets whether notifications should be delivered as aggregations or not.
*
* @param digestOn true to aggregate, false otherwise
*/
public void setDigestOn(boolean digestOn)
{
addField(SubscribeOptionFields.deliver, FormField.TYPE_BOOLEAN);
setAnswer(SubscribeOptionFields.deliver.getFieldName(), digestOn);
}
/**
* Gets the minimum number of milliseconds between sending notification digests
*
* @return The frequency in milliseconds
*/
public int getDigestFrequency()
{
return Integer.parseInt(getFieldValue(SubscribeOptionFields.digest_frequency));
}
/**
* Sets the minimum number of milliseconds between sending notification digests
*
* @param frequency The frequency in milliseconds
*/
public void setDigestFrequency(int frequency)
{
addField(SubscribeOptionFields.digest_frequency, FormField.TYPE_TEXT_SINGLE);
setAnswer(SubscribeOptionFields.digest_frequency.getFieldName(), frequency);
}
/**
* Get the time at which the leased subscription will expire, or has expired.
*
* @return The expiry date
*/
public Date getExpiry()
{
String dateTime = getFieldValue(SubscribeOptionFields.expire);
try
{
return format.parse(dateTime);
}
catch (ParseException e)
{
UnknownFormatConversionException exc = new UnknownFormatConversionException(dateTime);
exc.initCause(e);
throw exc;
}
}
/**
* Sets the time at which the leased subscription will expire, or has expired.
*
* @param expire The expiry date
*/
public void setExpiry(Date expire)
{
addField(SubscribeOptionFields.expire, FormField.TYPE_TEXT_SINGLE);
setAnswer(SubscribeOptionFields.expire.getFieldName(), format.format(expire));
}
/**
* Determines whether the entity wants to receive an XMPP message body in
* addition to the payload format.
*
* @return true to receive the message body, false otherwise
*/
public boolean isIncludeBody()
{
return parseBoolean(getFieldValue(SubscribeOptionFields.include_body));
}
/**
* Sets whether the entity wants to receive an XMPP message body in
* addition to the payload format.
*
* @param include true to receive the message body, false otherwise
*/
public void setIncludeBody(boolean include)
{
addField(SubscribeOptionFields.include_body, FormField.TYPE_BOOLEAN);
setAnswer(SubscribeOptionFields.include_body.getFieldName(), include);
}
/**
* Gets the {@link PresenceState} for which an entity wants to receive
* notifications.
*
* @return iterator over the list of states
*/
public Iterator<PresenceState> getShowValues()
{
ArrayList<PresenceState> result = new ArrayList<PresenceState>(5);
Iterator<String > it = getFieldValues(SubscribeOptionFields.show_values);
while (it.hasNext())
{
String state = it.next();
result.add(PresenceState.valueOf(state));
}
return result.iterator();
}
/**
* Sets the list of {@link PresenceState} for which an entity wants
* to receive notifications.
*
* @param stateValues The list of states
*/
public void setShowValues(Collection<PresenceState> stateValues)
{
ArrayList<String> values = new ArrayList<String>(stateValues.size());
for (PresenceState state : stateValues)
{
values.add(state.toString());
}
addField(SubscribeOptionFields.show_values, FormField.TYPE_LIST_MULTI);
setAnswer(SubscribeOptionFields.show_values.getFieldName(), values);
}
static private boolean parseBoolean(String fieldValue)
{
return ("1".equals(fieldValue) || "true".equals(fieldValue));
}
private String getFieldValue(SubscribeOptionFields field)
{
FormField formField = getField(field.getFieldName());
return formField.getValues().next();
}
private Iterator<String> getFieldValues(SubscribeOptionFields field)
{
FormField formField = getField(field.getFieldName());
return formField.getValues();
}
private void addField(SubscribeOptionFields nodeField, String type)
{
String fieldName = nodeField.getFieldName();
if (getField(fieldName) == null)
{
FormField field = new FormField(fieldName);
field.setType(type);
addField(field);
}
}
}

View File

@ -0,0 +1,99 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Calendar;
/**
* Defines the possible field options for a subscribe options form as defined
* by <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-subscribe">Section 16.4.2</a>.
*
* @author Robin Collier
*/
public enum SubscribeOptionFields
{
/**
* Whether an entity wants to receive or disable notifications
*
* <p><b>Value: boolean</b></p>
*/
deliver,
/**
* Whether an entity wants to receive digests (aggregations) of
* notifications or all notifications individually.
*
* <p><b>Value: boolean</b></p>
*/
digest,
/**
* The minimum number of seconds between sending any two notifications digests
*
* <p><b>Value: int</b></p>
*/
digest_frequency,
/**
* The DateTime at which a leased subsscription will end ro has ended.
*
* <p><b>Value: {@link Calendar}</b></p>
*/
expire,
/**
* Whether an entity wants to receive an XMPP message body in addition to
* the payload format.
*
* <p><b>Value: boolean</b></p>
*/
include_body,
/**
* The presence states for which an entity wants to receive notifications.
*
* <p><b>Value: {@link PresenceState}</b></p>
*/
show_values,
/**
*
*
* <p><b>Value: </b></p>
*/
subscription_type,
/**
*
* <p><b>Value: </b></p>
*/
subscription_depth;
public String getFieldName()
{
if (this == show_values)
return "pubsub#" + toString().replace('_', '-');
return "pubsub#" + toString();
}
static public SubscribeOptionFields valueOfFromElement(String elementName)
{
String portion = elementName.substring(elementName.lastIndexOf('#' + 1));
if ("show-values".equals(portion))
return show_values;
else
return valueOf(portion);
}
}

View File

@ -0,0 +1,160 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
/**
* Represents a subscription to node for both requests and replies.
*
* @author Robin Collier
*/
public class Subscription extends NodeExtension
{
protected String jid;
protected String id;
protected State state;
protected boolean configRequired = false;
public enum State
{
subscribed, unconfigured, pending, none
}
/**
* Used to constructs a subscription request to the root node with the specified
* JID.
*
* @param subscriptionJid The subscriber JID
*/
public Subscription(String subscriptionJid)
{
this(subscriptionJid, null, null, null);
}
/**
* Used to constructs a subscription request to the specified node with the specified
* JID.
*
* @param subscriptionJid The subscriber JID
* @param nodeId The node id
*/
public Subscription(String subscriptionJid, String nodeId)
{
this(subscriptionJid, nodeId, null, null);
}
/**
* Constructs a representation of a subscription reply to the specified node
* and JID. The server will have supplied the subscription id and current state.
*
* @param jid The JID the request was made under
* @param nodeId The node subscribed to
* @param subscriptionId The id of this subscription
* @param state The current state of the subscription
*/
public Subscription(String jid, String nodeId, String subscriptionId, State state)
{
super(PubSubElementType.SUBSCRIPTION, nodeId);
this.jid = jid;
id = subscriptionId;
this.state = state;
}
/**
* Constructs a representation of a subscription reply to the specified node
* and JID. The server will have supplied the subscription id and current state
* and whether the subscription need to be configured.
*
* @param jid The JID the request was made under
* @param nodeId The node subscribed to
* @param subscriptionId The id of this subscription
* @param state The current state of the subscription
* @param configRequired Is configuration required to complete the subscription
*/
public Subscription(String jid, String nodeId, String subscriptionId, State state, boolean configRequired)
{
super(PubSubElementType.SUBSCRIPTION, nodeId);
this.jid = jid;
id = subscriptionId;
this.state = state;
this.configRequired = configRequired;
}
/**
* Gets the JID the subscription is created for
*
* @return The JID
*/
public String getJid()
{
return jid;
}
/**
* Gets the subscription id
*
* @return The subscription id
*/
public String getId()
{
return id;
}
/**
* Gets the current subscription state.
*
* @return Current subscription state
*/
public State getState()
{
return state;
}
/**
* This value is only relevant when the {@link #getState()} is {@link State#unconfigured}
*
* @return true if configuration is required, false otherwise
*/
public boolean isConfigRequired()
{
return configRequired;
}
public String toXML()
{
StringBuilder builder = new StringBuilder("<subscription");
appendAttribute(builder, "jid", jid);
if (getNode() != null)
appendAttribute(builder, "node", getNode());
if (id != null)
appendAttribute(builder, "subid", id);
if (state != null)
appendAttribute(builder, "subscription", state.toString());
builder.append("/>");
return builder.toString();
}
private void appendAttribute(StringBuilder builder, String att, String value)
{
builder.append(" ");
builder.append(att);
builder.append("='");
builder.append(value);
builder.append("'");
}
}

View File

@ -0,0 +1,75 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Collections;
import java.util.List;
/**
* Base class to represents events that are associated to subscriptions.
*
* @author Robin Collier
*/
abstract public class SubscriptionEvent extends NodeEvent
{
private List<String> subIds = Collections.EMPTY_LIST;
/**
* Construct an event with no subscription id's. This can
* occur when there is only one subscription to a node. The
* event may or may not report the subscription id along
* with the event.
*
* @param nodeId The id of the node the event came from
*/
protected SubscriptionEvent(String nodeId)
{
super(nodeId);
}
/**
* Construct an event with multiple subscriptions.
*
* @param nodeId The id of the node the event came from
* @param subscriptionIds The list of subscription id's
*/
protected SubscriptionEvent(String nodeId, List<String> subscriptionIds)
{
super(nodeId);
if (subscriptionIds != null)
subIds = subscriptionIds;
}
/**
* Get the subscriptions this event is associated with.
*
* @return List of subscription id's
*/
public List<String> getSubscriptions()
{
return Collections.unmodifiableList(subIds);
}
/**
* Set the list of subscription id's for this event.
*
* @param subscriptionIds The list of subscription id's
*/
protected void setSubscriptions(List<String> subscriptionIds)
{
if (subscriptionIds != null)
subIds = subscriptionIds;
}
}

View File

@ -0,0 +1,96 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Collections;
import java.util.List;
/**
* Represents the element holding the list of subscription elements.
*
* @author Robin Collier
*/
public class SubscriptionsExtension extends NodeExtension
{
protected List<Subscription> items = Collections.EMPTY_LIST;
/**
* Subscriptions to the root node
*
* @param subList The list of subscriptions
*/
public SubscriptionsExtension(List<Subscription> subList)
{
super(PubSubElementType.SUBSCRIPTIONS);
if (subList != null)
items = subList;
}
/**
* Subscriptions to the specified node.
*
* @param nodeId The node subscribed to
* @param subList The list of subscriptions
*/
public SubscriptionsExtension(String nodeId, List<Subscription> subList)
{
super(PubSubElementType.SUBSCRIPTIONS, nodeId);
if (subList != null)
items = subList;
}
/**
* Gets the list of subscriptions.
*
* @return List of subscriptions
*/
public List<Subscription> getSubscriptions()
{
return items;
}
@Override
public String toXML()
{
if ((items == null) || (items.size() == 0))
{
return super.toXML();
}
else
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
if (getNode() != null)
{
builder.append(" node='");
builder.append(getNode());
builder.append("'");
}
builder.append(">");
for (Subscription item : items)
{
builder.append(item.toXML());
}
builder.append("</");
builder.append(getElementName());
builder.append(">");
return builder.toString();
}
}
}

View File

@ -0,0 +1,73 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smackx.pubsub.util.XmlUtils;
/**
* Represents an unsubscribe element.
*
* @author Robin Collier
*/
public class UnsubscribeExtension extends NodeExtension
{
protected String jid;
protected String id;
public UnsubscribeExtension(String subscriptionJid)
{
this(subscriptionJid, null, null);
}
public UnsubscribeExtension(String subscriptionJid, String nodeId)
{
this(subscriptionJid, nodeId, null);
}
public UnsubscribeExtension(String jid, String nodeId, String subscriptionId)
{
super(PubSubElementType.UNSUBSCRIBE, nodeId);
this.jid = jid;
id = subscriptionId;
}
public String getJid()
{
return jid;
}
public String getId()
{
return id;
}
@Override
public String toXML()
{
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
XmlUtils.appendAttribute(builder, "jid", jid);
if (getNode() != null)
XmlUtils.appendAttribute(builder, "node", getNode());
if (id != null)
XmlUtils.appendAttribute(builder, "subid", id);
builder.append("/>");
return builder.toString();
}
}

View File

@ -0,0 +1,41 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.listener;
import org.jivesoftware.smackx.pubsub.ItemDeleteEvent;
import org.jivesoftware.smackx.pubsub.LeafNode;
/**
* Defines the listener for item deletion events from a node.
*
* @see LeafNode#addItemDeleteListener(ItemDeleteListener)
*
* @author Robin Collier
*/
public interface ItemDeleteListener
{
/**
* Called when items are deleted from a node the listener is
* registered with.
*
* @param items The event with item deletion details
*/
void handleDeletedItems(ItemDeleteEvent items);
/**
* Called when <b>all</b> items are deleted from a node the listener is
* registered with.
*/
void handlePurge();
}

View File

@ -0,0 +1,36 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.listener;
import org.jivesoftware.smackx.pubsub.Item;
import org.jivesoftware.smackx.pubsub.ItemPublishEvent;
import org.jivesoftware.smackx.pubsub.LeafNode;
/**
* Defines the listener for items being published to a node.
*
* @see LeafNode#addItemEventListener(ItemEventListener)
*
* @author Robin Collier
*/
public interface ItemEventListener <T extends Item>
{
/**
* Called whenever an item is published to the node the listener
* is registered with.
*
* @param items The publishing details.
*/
void handlePublishedItems(ItemPublishEvent<T> items);
}

View File

@ -0,0 +1,35 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.listener;
import org.jivesoftware.smackx.pubsub.ConfigurationEvent;
import org.jivesoftware.smackx.pubsub.LeafNode;
/**
* Defines the listener for a node being configured.
*
* @see LeafNode#addConfigurationListener(NodeConfigListener)
*
* @author Robin Collier
*/
public interface NodeConfigListener
{
/**
* Called whenever the node the listener
* is registered with is configured.
*
* @param config The configuration details.
*/
void handleNodeConfiguration(ConfigurationEvent config);
}

View File

@ -0,0 +1,106 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.packet;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.pubsub.PubSubElementType;
/**
* The standard PubSub extension of an {@link IQ} packet. This is the topmost
* element of all pubsub requests and replies as defined in the <a href="http://xmpp.org/extensions/xep-0060">Publish-Subscribe</a>
* specification.
*
* @author Robin Collier
*/
public class PubSub extends IQ
{
private PubSubNamespace ns = PubSubNamespace.BASIC;
/**
* Returns the XML element name of the extension sub-packet root element.
*
* @return the XML element name of the packet extension.
*/
public String getElementName() {
return "pubsub";
}
/**
* Returns the XML namespace of the extension sub-packet root element.
* According the specification the namespace is
* http://jabber.org/protocol/pubsub with a specific fragment depending
* on the request. The namespace is defined at <a href="http://xmpp.org/registrar/namespaces.html">XMPP Registrar</a> at
*
* The default value has no fragment.
*
* @return the XML namespace of the packet extension.
*/
public String getNamespace()
{
return ns.getXmlns();
}
/**
* Set the namespace for the packet if it something other than the default
* case of {@link PubSubNamespace#BASIC}. The {@link #getNamespace()} method will return
* the result of calling {@link PubSubNamespace#getXmlns()} on the specified enum.
*
* @param ns - The new value for the namespace.
*/
public void setPubSubNamespace(PubSubNamespace ns)
{
this.ns = ns;
}
public PacketExtension getExtension(PubSubElementType elem)
{
return getExtension(elem.getElementName(), elem.getNamespace().getXmlns());
}
/**
* Returns the current value of the namespace. The {@link #getNamespace()} method will return
* the result of calling {@link PubSubNamespace#getXmlns()} this value.
*
* @return The current value of the namespace.
*/
public PubSubNamespace getPubSubNamespace()
{
return ns;
}
/**
* Returns the XML representation of a pubsub element according the specification.
*
* The XML representation will be inside of an iq packet like
* in the following example:
* <pre>
* &lt;iq type='set' id="MlIpV-4" to="pubsub.gato.home" from="gato3@gato.home/Smack"&gt;
* &lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt;
* :
* Specific request extension
* :
* &lt;/pubsub&gt;
* &lt;/iq&gt;
* </pre>
*
*/
public String getChildElementXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">");
buf.append(getExtensionsXML());
buf.append("</").append(getElementName()).append(">");
return buf.toString();
}
}

View File

@ -0,0 +1,63 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.packet;
/**
* Defines all the valid namespaces that are used with the {@link PubSub} packet
* as defined by the specification.
*
* @author Robin Collier
*/
public enum PubSubNamespace
{
BASIC(null),
ERROR("errors"),
EVENT("event"),
OWNER("owner");
private String fragment;
private PubSubNamespace(String fragment)
{
this.fragment = fragment;
}
public String getXmlns()
{
String ns = "http://jabber.org/protocol/pubsub";
if (fragment != null)
ns += '#' + fragment;
return ns;
}
public String getFragment()
{
return fragment;
}
public static PubSubNamespace valueOfFromXmlns(String ns)
{
int index = ns.lastIndexOf('#');
if (index != -1)
{
String suffix = ns.substring(ns.lastIndexOf('#')+1);
return valueOf(suffix.toUpperCase());
}
else
return BASIC;
}
}

View File

@ -0,0 +1,63 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.packet;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.packet.Packet;
/**
* Utility class for doing synchronous calls to the server. Provides several
* methods for sending a packet to the server and waiting for the reply.
*
* @author Robin Collier
*/
final public class SyncPacketSend
{
private SyncPacketSend()
{ }
static public Packet getReply(XMPPConnection connection, Packet packet, long timeout)
throws XMPPException
{
PacketFilter responseFilter = new PacketIDFilter(packet.getPacketID());
PacketCollector response = connection.createPacketCollector(responseFilter);
connection.sendPacket(packet);
// Wait up to a certain number of seconds for a reply.
Packet result = response.nextResult(timeout);
// Stop queuing results
response.cancel();
if (result == null) {
throw new XMPPException("No response from server.");
}
else if (result.getError() != null) {
throw new XMPPException(result.getError());
}
return result;
}
static public Packet getReply(XMPPConnection connection, Packet packet)
throws XMPPException
{
return getReply(connection, packet, SmackConfiguration.getPacketReplyTimeout());
}
}

View File

@ -0,0 +1,37 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.Affiliation;
/**
* Parses the affiliation element out of the reply stanza from the server
* as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">affiliation schema</a>.
*
* @author Robin Collier
*/
public class AffiliationProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
{
return new Affiliation(attributeMap.get("node"), Affiliation.Type.valueOf(attributeMap.get("affiliation")));
}
}

View File

@ -0,0 +1,38 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.Affiliation;
import org.jivesoftware.smackx.pubsub.AffiliationsExtension;
/**
* Parses the affiliations element out of the reply stanza from the server
* as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">affiliation schema</a>.
*
* @author Robin Collier
*/public class AffiliationsProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
{
return new AffiliationsExtension((List<Affiliation>)content);
}
}

View File

@ -0,0 +1,42 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.packet.DataForm;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.ConfigurationEvent;
import org.jivesoftware.smackx.pubsub.ConfigureForm;
/**
* Parses the node configuration element out of the message event stanza from
* the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">configuration schema</a>.
*
* @author Robin Collier
*/
public class ConfigEventProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends PacketExtension> content)
{
if (content.size() == 0)
return new ConfigurationEvent(attMap.get("node"));
else
return new ConfigurationEvent(attMap.get("node"), new ConfigureForm((DataForm)content.iterator().next()));
}
}

View File

@ -0,0 +1,38 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.EventElement;
import org.jivesoftware.smackx.pubsub.EventElementType;
import org.jivesoftware.smackx.pubsub.NodeExtension;
/**
* Parses the event element out of the message stanza from
* the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">event schema</a>.
*
* @author Robin Collier
*/
public class EventProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends PacketExtension> content)
{
return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension)content.get(0));
}
}

View File

@ -0,0 +1,80 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.pubsub.Item;
import org.jivesoftware.smackx.pubsub.PayloadItem;
import org.jivesoftware.smackx.pubsub.SimplePayload;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.xmlpull.v1.XmlPullParser;
/**
* Parses an <b>item</b> element as is defined in both the {@link PubSubNamespace#BASIC} and {@link PubSubNamespace#EVENT}
* namespaces. To parse the item contents, it will use whatever {@link PacketExtensionProvider} is registered in
* <b>smack.providers</b> for its element name and namespace. If no provider is registered, it will return a {@link SimplePayload}.
*
* @author Robin Collier
*/
public class ItemProvider implements PacketExtensionProvider
{
public PacketExtension parseExtension(XmlPullParser parser) throws Exception
{
String id = parser.getAttributeValue(null, "id");
String elem = parser.getName();
int tag = parser.next();
if (tag == XmlPullParser.END_TAG)
{
return new Item(id);
}
else
{
String payloadElemName = parser.getName();
String payloadNS = parser.getNamespace();
if (ProviderManager.getInstance().getExtensionProvider(payloadElemName, payloadNS) == null)
{
boolean done = false;
String payloadText = null;
while (!done)
{
if (tag == XmlPullParser.END_TAG && parser.getName().equals(elem))
{
done = true;
}
else if (!((tag == XmlPullParser.START_TAG) && parser.isEmptyElementTag()))
{
if (payloadText == null)
payloadText = parser.getText();
else
payloadText += parser.getText();
}
tag = parser.next();
}
return new PayloadItem<SimplePayload>(id, new SimplePayload(payloadElemName, payloadNS, payloadText));
}
else
{
return new PayloadItem<PacketExtension>(id, PacketParserUtils.parsePacketExtension(payloadElemName, payloadNS, parser));
}
}
}
}

View File

@ -0,0 +1,38 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.ItemsExtension;
/**
* Parses the <b>items</b> element out of the message event stanza from
* the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">items schema</a>.
*
* @author Robin Collier
*/
public class ItemsProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
{
return new ItemsExtension(ItemsExtension.ItemsElementType.items, attributeMap.get("node"), content, null);
}
}

View File

@ -0,0 +1,62 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.xmlpull.v1.XmlPullParser;
/**
* Parses the root pubsub packet extensions of the {@link IQ} packet and returns
* a {@link PubSub} instance.
*
* @author Robin Collier
*/
public class PubSubProvider implements IQProvider
{
public IQ parseIQ(XmlPullParser parser) throws Exception
{
PubSub pubsub = new PubSub();
String namespace = parser.getNamespace();
pubsub.setPubSubNamespace(PubSubNamespace.valueOfFromXmlns(namespace));
boolean done = false;
while (!done)
{
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG)
{
PacketExtension ext = PacketParserUtils.parsePacketExtension(parser.getName(), namespace, parser);
if (ext != null)
{
pubsub.addExtension(ext);
}
}
else if (eventType == XmlPullParser.END_TAG)
{
if (parser.getName().equals("pubsub"))
{
done = true;
}
}
}
return pubsub;
}
}

View File

@ -0,0 +1,38 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.RetractItem;
/**
* Parses the <b>retract</b> element out of the message event stanza from
* the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">retract schema</a>.
* This element is a child of the <b>items</b> element.
*
* @author Robin Collier
*/
public class RetractEventProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
{
return new RetractItem(attributeMap.get("id"));
}
}

View File

@ -0,0 +1,37 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.NodeExtension;
import org.jivesoftware.smackx.pubsub.PubSubElementType;
/**
* Parses simple elements that only contain a <b>node</b> attribute. This is common amongst many of the
* elements defined in the pubsub specification. For this common case a {@link NodeExtension} is returned.
*
* @author Robin Collier
*/
public class SimpleNodeProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
{
return new NodeExtension(PubSubElementType.valueOfFromElemName(currentElement, currentNamespace), attributeMap.get("node"));
}
}

View File

@ -0,0 +1,52 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smackx.pubsub.Subscription;
import org.xmlpull.v1.XmlPullParser;
/**
* Parses the <b>subscription</b> element out of the pubsub IQ message from
* the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">subscription schema</a>.
*
* @author Robin Collier
*/
public class SubscriptionProvider implements PacketExtensionProvider
{
public PacketExtension parseExtension(XmlPullParser parser) throws Exception
{
String jid = parser.getAttributeValue(null, "jid");
String nodeId = parser.getAttributeValue(null, "node");
String subId = parser.getAttributeValue(null, "subid");
String state = parser.getAttributeValue(null, "subscription");
boolean isRequired = false;
int tag = parser.next();
if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options"))
{
tag = parser.next();
if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))
isRequired = true;
while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");
}
while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();
return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state)), isRequired);
}
}

View File

@ -0,0 +1,38 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.provider;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
import org.jivesoftware.smackx.pubsub.Subscription;
import org.jivesoftware.smackx.pubsub.SubscriptionsExtension;
/**
* Parses the <b>subscriptions</b> element out of the pubsub IQ message from
* the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">subscriptions schema</a>.
*
* @author Robin Collier
*/
public class SubscriptionsProvider extends EmbeddedExtensionProvider
{
@Override
protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
{
return new SubscriptionsExtension(attributeMap.get("node"), (List<Subscription>)content);
}
}

View File

@ -0,0 +1,43 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.util;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.pubsub.ConfigureForm;
import org.jivesoftware.smackx.pubsub.FormNode;
import org.jivesoftware.smackx.pubsub.PubSubElementType;
/**
* Utility for extracting information from packets.
*
* @author Robin Collier
*/
public class NodeUtils
{
/**
* Get a {@link ConfigureForm} from a packet.
*
* @param packet
* @param elem
* @return The configuration form
*/
public static ConfigureForm getFormFromPacket(Packet packet, PubSubElementType elem)
{
FormNode config = (FormNode)packet.getExtension(elem.getElementName(), elem.getNamespace().getXmlns());
Form formReply = config.getForm();
return new ConfigureForm(formReply);
}
}

View File

@ -0,0 +1,67 @@
/**
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.pubsub.util;
import java.io.StringReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
/**
* Simple utility for pretty printing xml.
*
* @author Robin Collier
*/
public class XmlUtils
{
/**
*
* @param header Just a title for the stanza for readability. Single word no spaces since
* it is inserted as the root element in the output.
* @param xml The string to pretty print
*/
static public void prettyPrint(String header, String xml)
{
try
{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
if (header != null)
{
xml = "\n<" + header + ">" + xml + "</" + header + '>';
}
transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(System.out));
}
catch (Exception e)
{
System.out.println("Something wrong with xml in \n---------------\n" + xml + "\n---------------");
e.printStackTrace();
}
}
static public void appendAttribute(StringBuilder builder, String att, String value)
{
builder.append(" ");
builder.append(att);
builder.append("='");
builder.append(value);
builder.append("'");
}
}

View File

@ -0,0 +1,45 @@
/*
* Created on 2009-05-05
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
class CarExtension implements PacketExtension
{
private String color;
private int numTires;
public CarExtension(String col, int num)
{
color = col;
numTires = num;
}
public String getColor()
{
return color;
}
public int getNumTires()
{
return numTires;
}
public String getElementName()
{
return "car";
}
public String getNamespace()
{
return "pubsub:test:vehicle";
}
public String toXML()
{
return "<" + getElementName() + " xmlns='" + getNamespace() + "'><paint color='" +
getColor() + "'/><tires num='" + getNumTires() + "'/></" + getElementName() + ">";
}
}

View File

@ -0,0 +1,35 @@
/*
* Created on 2009-05-05
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.xmlpull.v1.XmlPullParser;
public class CarExtensionProvider implements PacketExtensionProvider
{
public PacketExtension parseExtension(XmlPullParser parser) throws Exception
{
String color = null;
int numTires = 0;
for (int i=0; i<2; i++)
{
while (parser.next() != XmlPullParser.START_TAG);
if (parser.getName().equals("paint"))
{
color = parser.getAttributeValue(0);
}
else
{
numTires = Integer.parseInt(parser.getAttributeValue(0));
}
}
while (parser.next() != XmlPullParser.END_TAG);
return new CarExtension(color, numTires);
}
}

View File

@ -0,0 +1,72 @@
/*
* Created on 2009-04-09
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Iterator;
import java.util.List;
import org.jivesoftware.smackx.packet.DiscoverInfo;
import org.jivesoftware.smackx.packet.DiscoverItems;
import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase;
import org.jivesoftware.smackx.pubsub.util.XmlUtils;
public class EntityUseCases extends SingleUserTestCase
{
public void testDiscoverPubsubInfo() throws Exception
{
DiscoverInfo supportedFeatures = getManager().getSupportedFeatures();
assertNotNull(supportedFeatures);
}
public void testDiscoverNodeInfo() throws Exception
{
LeafNode myNode = getManager().createNode("DiscoNode" + System.currentTimeMillis());
DiscoverInfo info = myNode.discoverInfo();
assertTrue(info.getIdentities().hasNext());
Identity ident = info.getIdentities().next();
assertEquals("leaf", ident.getType());
}
public void testDiscoverNodeItems() throws Exception
{
LeafNode myNode = getRandomPubnode(getManager(), true, false);
myNode.send(new Item());
myNode.send(new Item());
myNode.send(new Item());
myNode.send(new Item());
DiscoverItems items = myNode.discoverItems();
int count = 0;
for(Iterator it = items.getItems(); it.hasNext(); it.next(),count++);
assertEquals(4, count);
}
public void testDiscoverSubscriptions() throws Exception
{
getManager().getSubscriptions();
}
public void testDiscoverNodeSubscriptions() throws Exception
{
LeafNode myNode = getRandomPubnode(getManager(), true, true);
myNode.subscribe(getConnection(0).getUser());
List<Subscription> subscriptions = myNode.getSubscriptions();
assertTrue(subscriptions.size() < 3);
for (Subscription subscription : subscriptions)
{
assertNull(subscription.getNode());
}
}
public void testRetrieveAffiliation() throws Exception
{
getManager().getAffiliations();
}
}

View File

@ -0,0 +1,130 @@
/*
* Created on 2009-04-09
*/
package org.jivesoftware.smackx.pubsub;
import java.util.Collection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase;
public class OwnerUseCases extends SingleUserTestCase
{
public void testCreateInstantNode() throws Exception
{
LeafNode node = getManager().createNode();
assertNotNull(node);
assertNotNull(node.getId());
}
public void testCreateNamedNode() throws Exception
{
String id = "TestNamedNode" + System.currentTimeMillis();
LeafNode node = getManager().createNode(id);
assertEquals(id, node.getId());
}
public void testCreateConfiguredNode() throws Exception
{
// Generate reasonably unique for multiple tests
String id = "TestConfigNode" + System.currentTimeMillis();
// Create and configure a node
ConfigureForm form = new ConfigureForm(FormType.submit);
form.setAccessModel(AccessModel.open);
form.setDeliverPayloads(false);
form.setNotifyRetract(true);
form.setPersistentItems(true);
form.setPublishModel(PublishModel.open);
LeafNode node = (LeafNode)getManager().createNode(id, form);
ConfigureForm currentForm = node.getNodeConfiguration();
assertEquals(AccessModel.open, currentForm.getAccessModel());
assertFalse(currentForm.isDeliverPayloads());
assertTrue(currentForm.isNotifyRetract());
assertTrue(currentForm.isPersistItems());
assertEquals(PublishModel.open, currentForm.getPublishModel());
}
public void testCreateAndUpdateConfiguredNode() throws Exception
{
// Generate reasonably unique for multiple tests
String id = "TestConfigNode2" + System.currentTimeMillis();
// Create and configure a node
ConfigureForm form = new ConfigureForm(FormType.submit);
form.setAccessModel(AccessModel.open);
form.setDeliverPayloads(false);
form.setNotifyRetract(true);
form.setPersistentItems(true);
form.setPublishModel(PublishModel.open);
LeafNode myNode = (LeafNode)getManager().createNode(id, form);
ConfigureForm config = myNode.getNodeConfiguration();
assertEquals(AccessModel.open, config.getAccessModel());
assertFalse(config.isDeliverPayloads());
assertTrue(config.isNotifyRetract());
assertTrue(config.isPersistItems());
assertEquals(PublishModel.open, config.getPublishModel());
ConfigureForm submitForm = new ConfigureForm(config.createAnswerForm());
submitForm.setAccessModel(AccessModel.whitelist);
submitForm.setDeliverPayloads(true);
submitForm.setNotifyRetract(false);
submitForm.setPersistentItems(false);
submitForm.setPublishModel(PublishModel.publishers);
myNode.sendConfigurationForm(submitForm);
ConfigureForm newConfig = myNode.getNodeConfiguration();
assertEquals(AccessModel.whitelist, newConfig.getAccessModel());
assertTrue(newConfig.isDeliverPayloads());
assertFalse(newConfig.isNotifyRetract());
assertFalse(newConfig.isPersistItems());
assertEquals(PublishModel.publishers, newConfig.getPublishModel());
}
public void testGetDefaultConfig() throws Exception
{
ConfigureForm form = getManager().getDefaultConfiguration();
assertNotNull(form);
}
public void testDeleteNode() throws Exception
{
LeafNode myNode = getManager().createNode();
assertNotNull(getManager().getNode(myNode.getId()));
getManager(0).deleteNode(myNode.getId());
try
{
assertNull(getManager().getNode(myNode.getId()));
fail("Node should not exist");
}
catch (XMPPException e)
{
}
}
public void testPurgeItems() throws XMPPException
{
LeafNode node = getRandomPubnode(getManager(), true, false);
node.send(new Item());
node.send(new Item());
node.send(new Item());
node.send(new Item());
node.send(new Item());
Collection<? extends Item> items = node.getItems();
assertTrue(items.size() == 5);
node.deleteAllItems();
items = node.getItems();
// Pubsub service may keep the last notification (in spec), so 0 or 1 may be returned on get items.
assertTrue(items.size() < 2);
}
}

View File

@ -0,0 +1,150 @@
/*
* Created on 2009-04-09
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.packet.XMPPError.Condition;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase;
public class PublisherUseCases extends SingleUserTestCase
{
public void testSendNodeTrNot() throws Exception
{
getPubnode(false, false).send();
}
public void testSendNodeTrPay_WithOutPayload() throws XMPPException
{
LeafNode node = getPubnode(false, true);
try
{
node.send(new Item());
fail("Exception should be thrown when there is no payload");
}
catch (XMPPException e) {
XMPPError err = e.getXMPPError();
assertTrue(err.getType().equals(XMPPError.Type.MODIFY));
assertTrue(err.getCondition().equals(Condition.bad_request.toString()));
assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns()));
}
try
{
node.send(new Item("test" + System.currentTimeMillis()));
fail("Exception should be thrown when there is no payload");
}
catch (XMPPException e) {
XMPPError err = e.getXMPPError();
assertTrue(err.getType().equals(XMPPError.Type.MODIFY));
assertTrue(err.getCondition().equals(Condition.bad_request.toString()));
assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns()));
}
}
public void testSendNodeTrPay_WithPayload() throws XMPPException
{
LeafNode node = getPubnode(false, true);
node.send(new PayloadItem<SimplePayload>(null,
new SimplePayload("book", "pubsub:test:book", "<book xmlns='pubsub:test:book'><title>Lord of the Rings</title></book>")));
node.send(new PayloadItem<SimplePayload>("test" + System.currentTimeMillis(),
new SimplePayload("book", "pubsub:test:book", "<book xmlns='pubsub:test:book'><title>Two Towers</title></book>")));
}
public void testSendNodePerNot() throws Exception
{
LeafNode node = getPubnode(true, false);
node.send(new Item());
node.send(new Item("test" + System.currentTimeMillis()));
node.send(new PayloadItem<SimplePayload>(null,
new SimplePayload("book", "pubsub:test:book", "<book xmlns='pubsub:test:book'><title>Lord of the Rings</title></book>")));
node.send(new PayloadItem<SimplePayload>("test" + System.currentTimeMillis(),
new SimplePayload("book", "pubsub:test:book", "<book xmlns='pubsub:test:book'><title>Two Towers</title></book>")));
}
public void testSendPerPay_WithPayload() throws Exception
{
LeafNode node = getPubnode(true, true);
node.send(new PayloadItem<SimplePayload>(null,
new SimplePayload("book", "pubsub:test:book", "<book xmlns='pubsub:test:book'><title>Lord of the Rings</title></book>")));
node.send(new PayloadItem<SimplePayload>("test" + System.currentTimeMillis(),
new SimplePayload("book", "pubsub:test:book", "<book xmlns='pubsub:test:book'><title>Two Towers</title></book>")));
}
public void testSendPerPay_NoPayload() throws Exception
{
LeafNode node = getPubnode(true, true);
try
{
node.send(new Item());
fail("Exception should be thrown when there is no payload");
}
catch (XMPPException e) {
XMPPError err = e.getXMPPError();
assertTrue(err.getType().equals(XMPPError.Type.MODIFY));
assertTrue(err.getCondition().equals(Condition.bad_request.toString()));
assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns()));
}
try
{
node.send(new Item("test" + System.currentTimeMillis()));
fail("Exception should be thrown when there is no payload");
}
catch (XMPPException e) {
XMPPError err = e.getXMPPError();
assertTrue(err.getType().equals(XMPPError.Type.MODIFY));
assertTrue(err.getCondition().equals(Condition.bad_request.toString()));
assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns()));
}
}
public void testDeleteItems() throws XMPPException
{
LeafNode node = getPubnode(true, false);
node.send(new Item("1"));
node.send(new Item("2"));
node.send(new Item("3"));
node.send(new Item("4"));
node.deleteItem("1");
Collection<? extends Item> items = node.getItems();
assertTrue(items.size() == 3);
assertEquals(items.iterator().next().getId(), "2");
}
public void testPersistItems() throws XMPPException
{
LeafNode node = getPubnode(true, false);
node.send(new Item("1"));
node.send(new Item("2"));
node.send(new Item("3"));
node.send(new Item("4"));
Collection<? extends Item> items = node.getItems();
assertTrue(items.size() == 4);
}
public void testItemOverwritten() throws XMPPException
{
LeafNode node = getPubnode(true, false);
node.send(new PayloadItem<SimplePayload>("1", new SimplePayload("test", null, "<test/>")));
node.send(new PayloadItem<SimplePayload>("1", new SimplePayload("test2", null, "<test2/>")));
List<? extends Item> items = node.getItems();
assertEquals(1, items.size());
assertEquals("1", items.get(0).getId());
}
}

View File

@ -0,0 +1,224 @@
/*
* Created on 2009-04-09
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.FormField;
import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase;
public class SubscriberUseCases extends SingleUserTestCase
{
public void testSubscribe() throws Exception
{
LeafNode node = getPubnode(false, false);
Subscription sub = node.subscribe(getJid());
assertEquals(getJid(), sub.getJid());
assertNotNull(sub.getId());
assertEquals(node.getId(), sub.getNode());
assertEquals(Subscription.State.subscribed, sub.getState());
}
public void testSubscribeBadJid() throws Exception
{
LeafNode node = getPubnode(false, false);
try
{
node.subscribe("this@over.here");
fail();
}
catch (XMPPException e)
{
}
}
public void testSubscribeWithOptions() throws Exception
{
SubscribeForm form = new SubscribeForm(FormType.submit);
form.setDeliverOn(true);
Calendar expire = Calendar.getInstance();
expire.set(2020, 1, 1);
form.setExpiry(expire.getTime());
LeafNode node = getPubnode(false, false);
node.subscribe(getJid(), form);
}
public void testSubscribeConfigRequired() throws Exception
{
ConfigureForm form = new ConfigureForm(FormType.submit);
form.setAccessModel(AccessModel.open);
// Openfire specific field - nothing in the spec yet
FormField required = new FormField("pubsub#subscription_required");
required.setType(FormField.TYPE_BOOLEAN);
form.addField(required);
form.setAnswer("pubsub#subscription_required", true);
LeafNode node = (LeafNode)getManager().createNode("Pubnode" + System.currentTimeMillis(), form);
Subscription sub = node.subscribe(getJid());
assertEquals(getJid(), sub.getJid());
assertNotNull(sub.getId());
assertEquals(node.getId(), sub.getNode());
assertEquals(true, sub.isConfigRequired());
}
public void testUnsubscribe() throws Exception
{
LeafNode node = getPubnode(false, false);
node.subscribe(getJid());
Collection<Subscription> subs = node.getSubscriptions();
node.unsubscribe(getJid());
Collection<Subscription> afterSubs = node.getSubscriptions();
assertEquals(subs.size()-1, afterSubs.size());
}
public void testUnsubscribeWithMultipleNoSubId() throws Exception
{
LeafNode node = getPubnode(false, false);
node.subscribe(getBareJID(0));
node.subscribe(getBareJID(0));
node.subscribe(getBareJID(0));
try
{
node.unsubscribe(getBareJID(0));
fail("Unsubscribe with no subid should fail");
}
catch (XMPPException e)
{
}
}
public void testUnsubscribeWithMultipleWithSubId() throws Exception
{
LeafNode node = getPubnode(false, false);
node.subscribe(getJid());
Subscription sub = node.subscribe(getJid());
node.subscribe(getJid());
node.unsubscribe(getJid(), sub.getId());
}
public void testGetOptions() throws Exception
{
LeafNode node = getPubnode(false, false);
Subscription sub = node.subscribe(getJid());
SubscribeForm form = node.getSubscriptionOptions(getJid(), sub.getId());
assertNotNull(form);
}
// public void testSubscribeWithConfig() throws Exception
// {
// LeafNode node = getPubnode(false, false);
//
// Subscription sub = node.subscribe(getBareJID(0));
//
// assertEquals(getBareJID(0), sub.getJid());
// assertNotNull(sub.getId());
// assertEquals(node.getId(), sub.getNode());
// assertEquals(true, sub.isConfigRequired());
// }
//
public void testGetItems() throws XMPPException
{
LeafNode node = getPubnode(true, false);
node.send((Item)null);
node.send((Item)null);
node.send((Item)null);
node.send((Item)null);
node.send((Item)null);
Collection<? extends Item> items = node.getItems();
assertTrue(items.size() == 5);
long curTime = System.currentTimeMillis();
node.send(new Item("1-" + curTime));
node.send(new Item("2-" + curTime));
node.send(new Item("3-" + curTime));
node.send(new Item("4-" + curTime));
node.send(new Item("5-" + curTime));
items = node.getItems();
assertTrue(items.size() == 10);
LeafNode payloadNode = getPubnode(true, true);
Map<String , String> idPayload = new HashMap<String, String>();
idPayload.put("6-" + curTime, "<a/>");
idPayload.put("7-" + curTime, "<a href=\"/up/here\"/>");
idPayload.put("8-" + curTime, "<entity>text<inner></inner></entity>");
idPayload.put("9-" + curTime, "<entity><inner><text></text></inner></entity>");
for (Map.Entry<String, String> payload : idPayload.entrySet())
{
payloadNode.send(new PayloadItem<SimplePayload>(payload.getKey(), new SimplePayload("a", "pubsub:test", payload.getValue())));
}
payloadNode.send(new PayloadItem<SimplePayload>("6-" + curTime, new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test'/>")));
payloadNode.send(new PayloadItem<SimplePayload>("7-" + curTime, new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test' href=\"/up/here\"/>")));
payloadNode.send(new PayloadItem<SimplePayload>("8-" + curTime, new SimplePayload("entity", "pubsub:test", "<entity xmlns='pubsub:test'>text<inner></inner></entity>")));
payloadNode.send(new PayloadItem<SimplePayload>("9-" + curTime, new SimplePayload("entity", "pubsub:test", "<entity xmlns='pubsub:test'><inner><text></text></inner></entity>")));
Collection<PayloadItem<SimplePayload>> payloadItems = payloadNode.getItems();
assertTrue(payloadItems.size() == 4);
}
public void getSpecifiedItems() throws XMPPException
{
LeafNode node = getPubnode(true, true);
node.send(new PayloadItem<SimplePayload>("1", new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test' href='/1'/>")));
node.send(new PayloadItem<SimplePayload>("2", new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test' href='/2'/>")));
node.send(new PayloadItem<SimplePayload>("3", new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test' href='/3'/>")));
node.send(new PayloadItem<SimplePayload>("4", new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test' href='/4'/>")));
node.send(new PayloadItem<SimplePayload>("5", new SimplePayload("a", "pubsub:test", "<a xmlns='pubsub:test' href='/5'/>")));
Collection<String> ids = new ArrayList<String>(3);
ids.add("1");
ids.add("3");
ids.add("4");
List<PayloadItem<SimplePayload>> items = node.getItems(ids);
assertEquals(3, items.size());
assertEquals(items.get(0).getId(), "1");
assertEquals(items.get(0).getPayload().toXML(), "<a xmlns='pubsub:test' href='/1'/>");
assertEquals(items.get(1).getId(), "3");
assertEquals(items.get(1).getPayload().toXML(), "<a xmlns='pubsub:test' href='/3'/>");
assertEquals(items.get(2).getId(), "5");
assertEquals(items.get(2).getPayload().toXML(), "<a xmlns='pubsub:test' href='/5'/>");
}
public void testGetLastNItems() throws XMPPException
{
LeafNode node = getPubnode(true, false);
node.send(new Item("1"));
node.send(new Item("2"));
node.send(new Item("3"));
node.send(new Item("4"));
node.send(new Item("5"));
List<Item> items = node.getItems(2);
assertEquals(2, items.size());
assertEquals(items.get(0).getId(), "4");
assertEquals(items.get(1).getId(), "5");
}
private String getJid()
{
return getConnection(0).getUser();
}
}

View File

@ -0,0 +1,22 @@
/*
* Created on 2009-04-09
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase;
public class TestAPI extends SingleUserTestCase
{
public void testGetNonexistentNode()
{
try
{
getManager().getNode("" + System.currentTimeMillis());
assertTrue(false);
}
catch (XMPPException e)
{
}
}
}

View File

@ -0,0 +1,575 @@
/*
* Created on 2009-04-22
*/
package org.jivesoftware.smackx.pubsub;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.XMPPError.Type;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;
import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;
public class TestEvents extends SmackTestCase
{
public TestEvents(String str)
{
super(str);
}
@Override
protected int getMaxConnections()
{
return 2;
}
private String getService()
{
return "pubsub." + getServiceName();
}
public void testCreateAndGetNode() throws Exception
{
String nodeId = "MyTestNode";
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = null;
try
{
creatorNode = (LeafNode)creatorMgr.getNode(nodeId);
}
catch (XMPPException e)
{
if (e.getXMPPError().getType() == Type.CANCEL && e.getXMPPError().getCondition().equals("item-not-found"))
creatorNode = creatorMgr.createNode(nodeId);
else
throw e;
}
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
assertNotNull(subNode);
}
public void testConfigureAndNotify() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, false, true);
BlockingQueue<NodeConfigCoordinator> queue = new ArrayBlockingQueue<NodeConfigCoordinator>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
NodeConfigListener sub1Handler = new NodeConfigCoordinator(queue, "sub1");
subNode.subscribe(getConnection(1).getUser());
subNode.addConfigurationListener(sub1Handler);
ConfigureForm currentConfig = creatorNode.getNodeConfiguration();
ConfigureForm form = new ConfigureForm(currentConfig.createAnswerForm());
form.setPersistentItems(true);
form.setDeliverPayloads(false);
form.setNotifyConfig(true);
creatorNode.sendConfigurationForm(form);
ConfigurationEvent event = queue.poll(5, TimeUnit.SECONDS).event;
assertEquals(nodeId, event.getNode());
assertNull(event.getConfiguration());
currentConfig = creatorNode.getNodeConfiguration();
form = new ConfigureForm(currentConfig.createAnswerForm());
form.setDeliverPayloads(true);
creatorNode.sendConfigurationForm(form);
event = queue.poll(5, TimeUnit.SECONDS).event;
assertEquals(nodeId, event.getNode());
assertNotNull(event.getConfiguration());
assertTrue(event.getConfiguration().isPersistItems());
assertTrue(event.getConfiguration().isDeliverPayloads());
}
public void testSendAndReceiveNoPayload() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
BlockingQueue<ItemEventCoordinator<Item>> queue = new ArrayBlockingQueue<ItemEventCoordinator<Item>>(3);
ItemEventCoordinator<Item> creatorHandler = new ItemEventCoordinator<Item>(queue, "creator");
creatorNode.addItemEventListener(creatorHandler);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemEventCoordinator<Item> sub1Handler = new ItemEventCoordinator<Item>(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
creatorNode.send(new Item(itemId));
for(int i=0; i<2; i++)
{
ItemEventCoordinator<Item> coord = queue.take();
assertEquals(1, coord.events.getItems().size());
assertEquals(itemId, coord.events.getItems().iterator().next().getId());
}
}
public void testPublishAndReceiveNoPayload() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
BlockingQueue<ItemEventCoordinator<Item>> queue = new ArrayBlockingQueue<ItemEventCoordinator<Item>>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemEventCoordinator<Item> sub1Handler = new ItemEventCoordinator<Item>(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
creatorNode.publish(new Item(itemId));
ItemEventCoordinator<Item> coord = queue.take();
assertEquals(1, coord.events.getItems().size());
assertEquals(itemId, coord.events.getItems().get(0).getId());
}
public void testSendAndReceiveSimplePayload() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true);
BlockingQueue<ItemEventCoordinator<PayloadItem<SimplePayload>>> queue = new ArrayBlockingQueue<ItemEventCoordinator<PayloadItem<SimplePayload>>>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemEventCoordinator<PayloadItem<SimplePayload>> sub1Handler = new ItemEventCoordinator<PayloadItem<SimplePayload>>(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
String payloadString = "<book xmlns=\"pubsub:test:book\"><author>Sir Arthur Conan Doyle</author></book>";
creatorNode.send(new PayloadItem<SimplePayload>(itemId, new SimplePayload("book", "pubsub:test:book", payloadString)));
ItemEventCoordinator<PayloadItem<SimplePayload>> coord = queue.take();
assertEquals(1, coord.events.getItems().size());
PayloadItem<SimplePayload> item = coord.events.getItems().get(0);
assertEquals(itemId, item.getId());
assertTrue(item.getPayload() instanceof SimplePayload);
assertEquals(payloadString, item.getPayload().toXML());
assertEquals("book", item.getPayload().getElementName());
}
/*
* For this test, the following extension needs to be added to the meta-inf/smack.providers file
*
* <extensionProvider>
* <elementName>car</elementName>
* <namespace>pubsub:test:vehicle</namespace>
* <className>org.jivesoftware.smackx.pubsub.CarExtensionProvider</className>
* </extensionProvider>
*/
/*
public void testSendAndReceiveCarPayload() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
Node creatorNode = getPubnode(creatorMgr, nodeId, true, true);
BlockingQueue<ItemEventCoordinator> queue = new ArrayBlockingQueue<ItemEventCoordinator>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
Node subNode = subMgr.getNode(nodeId);
ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
String payloadString = "<car xmlns='pubsub:test:vehicle'><paint color='green'/><tires num='4'/></car>";
creatorNode.send(new Item(itemId, new SimplePayload("car", "pubsub:test:vehicle", payloadString)));
ItemEventCoordinator coord = queue.take();
assertEquals(1, coord.events.getItems().size());
Item item = coord.events.getItems().get(0);
assertEquals(itemId, item.getId());
assertTrue(item.getPayload() instanceof CarExtension);
CarExtension car = (CarExtension)item.getPayload();
assertEquals("green", car.getColor());
assertEquals(4, car.getNumTires());
}
*/
public void testSendAndReceiveMultipleSubs() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
BlockingQueue<ItemEventCoordinator<Item>> queue = new ArrayBlockingQueue<ItemEventCoordinator<Item>>(3);
ItemEventCoordinator<Item> creatorHandler = new ItemEventCoordinator<Item>(queue, "creator");
creatorNode.addItemEventListener(creatorHandler);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemEventCoordinator<Item> sub1Handler = new ItemEventCoordinator<Item>(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
ItemEventCoordinator<Item> sub2Handler = new ItemEventCoordinator<Item>(queue, "sub2");
subNode.addItemEventListener(sub2Handler);
Subscription sub2 = subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
creatorNode.send(new Item(itemId));
for(int i=0; i<3; i++)
{
ItemEventCoordinator<Item> coord = queue.take();
assertEquals(1, coord.events.getItems().size());
assertEquals(itemId, coord.events.getItems().iterator().next().getId());
if (coord.id.equals("sub1") || coord.id.equals("sub2"))
{
assertEquals(2, coord.events.getSubscriptions().size());
}
}
}
public void testSendAndReceiveMultipleItems() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
BlockingQueue<ItemEventCoordinator<Item>> queue = new ArrayBlockingQueue<ItemEventCoordinator<Item>>(3);
ItemEventCoordinator<Item> creatorHandler = new ItemEventCoordinator<Item>(queue, "creator");
creatorNode.addItemEventListener(creatorHandler);
creatorNode.subscribe(getConnection(0).getUser());
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemEventCoordinator<Item> sub1Handler = new ItemEventCoordinator<Item>(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
ItemEventCoordinator<Item> sub2Handler = new ItemEventCoordinator<Item>(queue, "sub2");
subNode.addItemEventListener(sub2Handler);
Subscription sub2 = subNode.subscribe(getConnection(1).getUser());
assertEquals(Subscription.State.subscribed, sub1.getState());
assertEquals(Subscription.State.subscribed, sub2.getState());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
Collection<Item> items = new ArrayList<Item>(3);
items.add(new Item("First-" + itemId));
items.add(new Item("Second-" + itemId));
items.add(new Item("Third-" + itemId));
creatorNode.send(items);
for(int i=0; i<3; i++)
{
ItemEventCoordinator<Item> coord = queue.poll(5, TimeUnit.SECONDS);
if (coord == creatorHandler)
assertEquals(1, coord.events.getSubscriptions().size());
else
assertEquals(2, coord.events.getSubscriptions().size());
assertEquals(3, coord.events.getItems().size());
}
}
public void testSendAndReceiveDelayed() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
// Send event
String itemId = String.valueOf("DelayId-" + System.currentTimeMillis());
String payloadString = "<book xmlns='pubsub:test:book'><author>Sir Arthur Conan Doyle</author></book>";
creatorNode.send(new PayloadItem<SimplePayload>(itemId, new SimplePayload("book", "pubsub:test:book", payloadString)));
Thread.sleep(1000);
BlockingQueue<ItemEventCoordinator<PayloadItem<SimplePayload>>> queue = new ArrayBlockingQueue<ItemEventCoordinator<PayloadItem<SimplePayload>>>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemEventCoordinator<PayloadItem<SimplePayload>> sub1Handler = new ItemEventCoordinator<PayloadItem<SimplePayload>>(queue, "sub1");
subNode.addItemEventListener(sub1Handler);
Subscription sub1 = subNode.subscribe(getConnection(1).getUser());
ItemEventCoordinator<PayloadItem<SimplePayload>> coord = queue.take();
assertTrue(coord.events.isDelayed());
assertNotNull(coord.events.getPublishedDate());
}
public void testDeleteItemAndNotify() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
BlockingQueue<ItemDeleteCoordinator> queue = new ArrayBlockingQueue<ItemDeleteCoordinator>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemDeleteCoordinator sub1Handler = new ItemDeleteCoordinator(queue, "sub1");
subNode.addItemDeleteListener(sub1Handler);
subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
Collection<Item> items = new ArrayList<Item>(3);
String id1 = "First-" + itemId;
String id2 = "Second-" + itemId;
String id3 = "Third-" + itemId;
items.add(new Item(id1));
items.add(new Item(id2));
items.add(new Item(id3));
creatorNode.send(items);
creatorNode.deleteItem(id1);
ItemDeleteCoordinator coord = queue.poll(5, TimeUnit.SECONDS);
assertEquals(1, coord.event.getItemIds().size());
assertEquals(id1, coord.event.getItemIds().get(0));
creatorNode.deleteItem(Arrays.asList(id2, id3));
coord = queue.poll(5, TimeUnit.SECONDS);
assertEquals(2, coord.event.getItemIds().size());
assertTrue(coord.event.getItemIds().contains(id2));
assertTrue(coord.event.getItemIds().contains(id3));
}
public void testPurgeAndNotify() throws Exception
{
// Setup event source
String nodeId = "TestNode" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false);
BlockingQueue<ItemDeleteCoordinator> queue = new ArrayBlockingQueue<ItemDeleteCoordinator>(3);
// Setup event receiver
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode = (LeafNode)subMgr.getNode(nodeId);
ItemDeleteCoordinator sub1Handler = new ItemDeleteCoordinator(queue, "sub1");
subNode.addItemDeleteListener(sub1Handler);
subNode.subscribe(getConnection(1).getUser());
// Send event
String itemId = String.valueOf(System.currentTimeMillis());
Collection<Item> items = new ArrayList<Item>(3);
String id1 = "First-" + itemId;
String id2 = "Second-" + itemId;
String id3 = "Third-" + itemId;
items.add(new Item(id1));
items.add(new Item(id2));
items.add(new Item(id3));
creatorNode.send(items);
creatorNode.deleteAllItems();
ItemDeleteCoordinator coord = queue.poll(5, TimeUnit.SECONDS);
assertNull(nodeId, coord.event);
}
public void testListenerMultipleNodes() throws Exception
{
// Setup event source
String nodeId1 = "Node-1-" + System.currentTimeMillis();
PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService());
String nodeId2 = "Node-2-" + System.currentTimeMillis();
LeafNode creatorNode1 = getPubnode(creatorMgr, nodeId1, true, false);
LeafNode creatorNode2 = getPubnode(creatorMgr, nodeId2, true, false);
BlockingQueue<ItemEventCoordinator<Item>> queue = new ArrayBlockingQueue<ItemEventCoordinator<Item>>(3);
PubSubManager subMgr = new PubSubManager(getConnection(1), getService());
LeafNode subNode1 = (LeafNode)subMgr.getNode(nodeId1);
LeafNode subNode2 = (LeafNode)subMgr.getNode(nodeId2);
subNode1.addItemEventListener(new ItemEventCoordinator<Item>(queue, "sub1"));
subNode2.addItemEventListener(new ItemEventCoordinator<Item>(queue, "sub2"));
subNode1.subscribe(getConnection(1).getUser());
subNode2.subscribe(getConnection(1).getUser());
creatorNode1.send(new Item("item1"));
creatorNode2.send(new Item("item2"));
boolean check1 = false;
boolean check2 = false;
for (int i=0; i<2; i++)
{
ItemEventCoordinator<Item> event = queue.take();
if (event.id.equals("sub1"))
{
assertEquals(event.events.getNodeId(), nodeId1);
check1 = true;
}
else
{
assertEquals(event.events.getNodeId(), nodeId2);
check2 = true;
}
}
assertTrue(check1);
assertTrue(check2);
}
class ItemEventCoordinator <T extends Item> implements ItemEventListener<T>
{
private BlockingQueue<ItemEventCoordinator<T>> theQueue;
private ItemPublishEvent<T> events;
private String id;
ItemEventCoordinator(BlockingQueue<ItemEventCoordinator<T>> queue, String id)
{
theQueue = queue;
this.id = id;
}
@Override
public void handlePublishedItems(ItemPublishEvent<T> items)
{
events = items;
theQueue.add(this);
}
@Override
public String toString()
{
return "ItemEventCoordinator: " + id;
}
}
class NodeConfigCoordinator implements NodeConfigListener
{
private BlockingQueue<NodeConfigCoordinator> theQueue;
private String id;
private ConfigurationEvent event;
NodeConfigCoordinator(BlockingQueue<NodeConfigCoordinator> queue, String id)
{
theQueue = queue;
this.id = id;
}
public void handleNodeConfiguration(ConfigurationEvent config)
{
event = config;
theQueue.add(this);
}
@Override
public String toString()
{
return "NodeConfigCoordinator: " + id;
}
}
class ItemDeleteCoordinator implements ItemDeleteListener
{
private BlockingQueue<ItemDeleteCoordinator> theQueue;
private String id;
private ItemDeleteEvent event;
ItemDeleteCoordinator(BlockingQueue<ItemDeleteCoordinator> queue, String id)
{
theQueue = queue;
this.id = id;
}
public void handleDeletedItems(ItemDeleteEvent delEvent)
{
event = delEvent;
theQueue.add(this);
}
public void handlePurge()
{
event = null;
theQueue.add(this);
}
@Override
public String toString()
{
return "ItemDeleteCoordinator: " + id;
}
}
static private LeafNode getPubnode(PubSubManager manager, String id, boolean persistItems, boolean deliverPayload)
throws XMPPException
{
ConfigureForm form = new ConfigureForm(FormType.submit);
form.setPersistentItems(persistItems);
form.setDeliverPayloads(deliverPayload);
form.setAccessModel(AccessModel.open);
return (LeafNode)manager.createNode(id, form);
}
}

View File

@ -0,0 +1,101 @@
/*
* Created on 2009-08-05
*/
package org.jivesoftware.smackx.pubsub;
import junit.framework.TestCase;
public class TestMessageContent extends TestCase
{
String payloadXmlWithNS = "<book xmlns='pubsub:test:book'><author name='Stephen King'/></book>";
public void testItemWithId()
{
Item item = new Item("123");
assertEquals("<item id='123'/>", item.toXML());
assertEquals("item", item.getElementName());
assertNull(item.getNamespace());
}
public void testItemWithNoId()
{
Item item = new Item();
assertEquals("<item/>", item.toXML());
Item itemNull = new Item(null);
assertEquals("<item/>", itemNull.toXML());
}
public void testSimplePayload()
{
SimplePayload payloadNS = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS);
assertEquals(payloadXmlWithNS, payloadNS.toXML());
String payloadXmlWithNoNS = "<book><author name='Stephen King'/></book>";
SimplePayload payloadNoNS = new SimplePayload("book", null, "<book><author name='Stephen King'/></book>");
assertEquals(payloadXmlWithNoNS, payloadNoNS.toXML());
}
public void testPayloadItemWithId()
{
SimplePayload payload = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS);
PayloadItem<SimplePayload> item = new PayloadItem<SimplePayload>("123", payload);
String xml = "<item id='123'>" + payloadXmlWithNS + "</item>";
assertEquals(xml, item.toXML());
assertEquals("item", item.getElementName());
}
public void testPayloadItemWithNoId()
{
SimplePayload payload = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS);
PayloadItem<SimplePayload> item = new PayloadItem<SimplePayload>(null, payload);
String xml = "<item>" + payloadXmlWithNS + "</item>";
assertEquals(xml, item.toXML());
}
public void testPayloadItemWithIdNoPayload()
{
try
{
PayloadItem<SimplePayload> item = new PayloadItem<SimplePayload>("123", null);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException e)
{
}
}
public void testPayloadItemWithNoIdNoPayload()
{
try
{
PayloadItem<SimplePayload> item = new PayloadItem<SimplePayload>(null, null);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException e)
{
}
}
public void testRetractItem()
{
RetractItem item = new RetractItem("1234");
assertEquals("<retract id='1234'/>", item.toXML());
assertEquals("retract", item.getElementName());
try
{
new RetractItem(null);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException e)
{
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Created on 2009-05-05
*/
package org.jivesoftware.smackx.pubsub.test;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.pubsub.AccessModel;
import org.jivesoftware.smackx.pubsub.ConfigureForm;
import org.jivesoftware.smackx.pubsub.FormType;
import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jivesoftware.smackx.pubsub.PubSubManager;
abstract public class PubSubTestCase extends SmackTestCase
{
private PubSubManager[] manager;
public PubSubTestCase(String arg0)
{
super(arg0);
}
public PubSubTestCase()
{
super("PubSub Test Case");
}
protected LeafNode getRandomPubnode(PubSubManager pubMgr, boolean persistItems, boolean deliverPayload) throws XMPPException
{
ConfigureForm form = new ConfigureForm(FormType.submit);
form.setPersistentItems(persistItems);
form.setDeliverPayloads(deliverPayload);
form.setAccessModel(AccessModel.open);
return (LeafNode)pubMgr.createNode("/test/Pubnode" + System.currentTimeMillis(), form);
}
protected LeafNode getPubnode(PubSubManager pubMgr, boolean persistItems, boolean deliverPayload, String nodeId) throws XMPPException
{
LeafNode node = null;
try
{
node = (LeafNode)pubMgr.getNode(nodeId);
}
catch (XMPPException e)
{
ConfigureForm form = new ConfigureForm(FormType.submit);
form.setPersistentItems(persistItems);
form.setDeliverPayloads(deliverPayload);
form.setAccessModel(AccessModel.open);
node = (LeafNode)pubMgr.createNode(nodeId, form);
}
return node;
}
protected PubSubManager getManager(int idx)
{
if (manager == null)
{
manager = new PubSubManager[getMaxConnections()];
for(int i=0; i<manager.length; i++)
{
manager[i] = new PubSubManager(getConnection(i), getService());
}
}
return manager[idx];
}
protected String getService()
{
return "pubsub." + getServiceName();
}
}

View File

@ -0,0 +1,28 @@
/*
* Created on 2009-05-05
*/
package org.jivesoftware.smackx.pubsub.test;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jivesoftware.smackx.pubsub.PubSubManager;
public class SingleUserTestCase extends PubSubTestCase
{
protected PubSubManager getManager()
{
return getManager(0);
}
protected LeafNode getPubnode(boolean persistItems, boolean deliverPayload) throws XMPPException
{
return getRandomPubnode(getManager(), persistItems, deliverPayload);
}
@Override
protected int getMaxConnections()
{
return 1;
}
}