Commit 4fca9fe9 authored by Matt Tucker's avatar Matt Tucker Committed by matt

First pubsub code landing.

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3580 b35dd754-fafc-0310-a699-88a17e54d16e
parent a9ec7c81
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.pubsub;
import org.xmpp.packet.JID;
import org.xmpp.forms.FormField;
import org.xmpp.forms.DataForm;
import org.jivesoftware.util.LocaleUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* A type of node that contains nodes and/or other collections but no published
* items. Collections provide the foundation entity to provide a means of representing
* hierarchical node structures.
*
* @author Matt Tucker
*/
public class CollectionNode extends Node {
/**
* Map that contains the child nodes of this node. The key is the child node ID and the
* value is the child node. A map is used to ensure uniqueness and in particular
* a ConcurrentHashMap for concurrency reasons.
*/
private Map<String, Node> nodes = new ConcurrentHashMap<String, Node>();
/**
* Policy that defines who may associate leaf nodes with a collection.
*/
private LeafNodeAssociationPolicy associationPolicy = LeafNodeAssociationPolicy.all;
/**
* Users that are allowed to associate leaf nodes with this collection node. This collection
* is going to be used only when the associationPolicy is <tt>whitelist</tt>.
*/
private Collection<JID> associationTrusted = new ArrayList<JID>();
/**
* Max number of leaf nodes that this collection node might have. A value of -1 means
* that there is no limit.
*/
private int maxLeafNodes = -1;
// TODO Send event notification when a new child node is added (section 9.2)
// TODO Add checking that max number of leaf nodes has been reached
// TODO Add checking that verifies that user that is associating leaf node with collection node is allowed
CollectionNode(PubSubService service, CollectionNode parentNode, String nodeID, JID creator) {
super(service, parentNode, nodeID, creator);
// Configure node with default values (get them from the pubsub service)
DefaultNodeConfiguration defaultConfiguration = service.getDefaultNodeConfiguration(false);
this.associationPolicy = defaultConfiguration.getAssociationPolicy();
this.maxLeafNodes = defaultConfiguration.getMaxLeafNodes();
}
void configure(FormField field) {
List<String> values;
if ("pubsub#leaf_node_association_policy".equals(field.getVariable())) {
values = field.getValues();
if (values.size() > 0) {
associationPolicy = LeafNodeAssociationPolicy.valueOf(values.get(0));
}
}
else if ("pubsub#leaf_node_association_whitelist".equals(field.getVariable())) {
// Get the new list of users that may add leaf nodes to this collection node
associationTrusted = new ArrayList<JID>();
for (String value : field.getValues()) {
try {
associationTrusted.add(new JID(value));
}
catch (Exception e) {}
}
}
else if ("pubsub#leaf_nodes_max".equals(field.getVariable())) {
values = field.getValues();
maxLeafNodes = values.size() > 0 ? Integer.parseInt(values.get(0)) : -1;
}
}
void postConfigure(DataForm completedForm) {
//Do nothing.
}
protected void addFormFields(DataForm form, boolean isEditing) {
super.addFormFields(form, isEditing);
FormField formField = form.addField();
formField.setVariable("pubsub#leaf_node_association_policy");
if (isEditing) {
formField.setType(FormField.Type.list_single);
formField.setLabel(LocaleUtils.getLocalizedString("pubsub.form.conf.leaf_node_association"));
formField.addOption(null, LeafNodeAssociationPolicy.all.name());
formField.addOption(null, LeafNodeAssociationPolicy.owners.name());
formField.addOption(null, LeafNodeAssociationPolicy.whitelist.name());
}
formField.addValue(associationPolicy.name());
formField = form.addField();
formField.setVariable("pubsub#leaf_node_association_whitelist");
if (isEditing) {
formField.setType(FormField.Type.jid_multi);
formField.setLabel(LocaleUtils.getLocalizedString("pubsub.form.conf.leaf_node_whitelist"));
}
for (JID contact : associationTrusted) {
formField.addValue(contact.toString());
}
formField = form.addField();
formField.setVariable("pubsub#leaf_nodes_max");
if (isEditing) {
formField.setType(FormField.Type.text_single);
formField.setLabel(LocaleUtils.getLocalizedString("pubsub.form.conf.leaf_nodes_max"));
}
formField.addValue(maxLeafNodes);
}
void addChildNode(Node child) {
nodes.put(child.getNodeID(), child);
}
void removeChildNode(Node child) {
nodes.remove(child.getNodeID());
}
public boolean isCollectionNode() {
return true;
}
/**
* Returns true if the specified node is a first-level children of this collection
* node.
*
* @param child the node to check if it is a direct child of this node.
* @return true if the specified node is a first-level children of this collection
* node.
*/
public boolean isChildNode(Node child) {
return nodes.containsKey(child.getNodeID());
}
/**
* Returns true if the specified node is a direct child node of this collection node or
* a descendant of the children nodes.
*
* @param child the node to check if it is a descendant of this node.
* @return true if the specified node is a direct child node of this collection node or
* a descendant of the children nodes.
*/
public boolean isDescendantNode(Node child) {
if (isChildNode(child)) {
return true;
}
for (Node node : getNodes()) {
if (node.isDescendantNode(child)) {
return true;
}
}
return false;
}
public Collection<Node> getNodes() {
return nodes.values();
}
/**
* Returns the policy that defines who may associate leaf nodes with a collection.
*
* @return the policy that defines who may associate leaf nodes with a collection.
*/
public LeafNodeAssociationPolicy getAssociationPolicy() {
return associationPolicy;
}
/**
* Returns the users that are allowed to associate leaf nodes with this collection node.
* This collection is going to be used only when the associationPolicy is <tt>whitelist</tt>.
*
* @return the users that are allowed to associate leaf nodes with this collection node.
*/
public Collection<JID> getAssociationTrusted() {
return associationTrusted;
}
/**
* Returns the max number of leaf nodes that this collection node might have. A value of
* -1 means that there is no limit.
*
* @return the max number of leaf nodes that this collection node might have.
*/
public int getMaxLeafNodes() {
return maxLeafNodes;
}
/**
* Sets the policy that defines who may associate leaf nodes with a collection.
*
* @param associationPolicy the policy that defines who may associate leaf nodes
* with a collection.
*/
void setAssociationPolicy(LeafNodeAssociationPolicy associationPolicy) {
this.associationPolicy = associationPolicy;
}
/**
* Sets the users that are allowed to associate leaf nodes with this collection node.
* This collection is going to be used only when the associationPolicy is <tt>whitelist</tt>.
*
* @param associationTrusted the users that are allowed to associate leaf nodes with this
* collection node.
*/
void setAssociationTrusted(Collection<JID> associationTrusted) {
this.associationTrusted = associationTrusted;
}
/**
* Sets the max number of leaf nodes that this collection node might have. A value of
* -1 means that there is no limit.
*
* @param maxLeafNodes the max number of leaf nodes that this collection node might have.
*/
void setMaxLeafNodes(int maxLeafNodes) {
this.maxLeafNodes = maxLeafNodes;
}
/**
* Policy that defines who may associate leaf nodes with a collection.
*/
public static enum LeafNodeAssociationPolicy {
/**
* 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;
}
}
This diff is collapsed.
This diff is collapsed.
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.pubsub;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import java.util.*;
/**
* A NodeAffiliate keeps information about the affiliation of an entity with a node. Possible
* affiliations are: owner, publisher, none or outcast. All except for outcast affiliations
* may have a {@link NodeSubscription} with the node.
*
* @author Matt Tucker
*/
public class NodeAffiliate {
private JID jid;
private Node node;
private Affiliation affiliation;
NodeAffiliate(Node node, JID jid) {
this.node = node;
this.jid = jid;
}
public Node getNode() {
return node;
}
public JID getJID() {
return jid;
}
public Affiliation getAffiliation() {
return affiliation;
}
void setAffiliation(Affiliation affiliation) {
this.affiliation = affiliation;
}
/**
* Returns the list of subscriptions of the affiliate in the node.
*
* @return the list of subscriptions of the affiliate in the node.
*/
public Collection<NodeSubscription> getSubscriptions() {
return node.getSubscriptions(jid);
}
/**
* Sends an event notification to each affected subscription of the affiliate. If the owner
* has many subscriptions from the same full JID then a single notification is going to be
* sent including a detail of the subscription IDs for which the notification is being sent.<p>
*
* The original publication to the node may or may not contain a {@link PublishedItem}. The
* subscriptions of the affiliation will be filtered based on the published item (if one was
* specified), the subscription status and originating node.
*
* @param notification the message to send containing the event notification.
* @param node the node that received a new publication.
* @param publishedItem the item was was published in the publication or null if none
* was published.
*/
public void sendEventNotification(Message notification, LeafNode node,
PublishedItem publishedItem) {
// Filter affiliate subscriptions and only use approved and configured ones
List<NodeSubscription> notifySubscriptions = new ArrayList<NodeSubscription>();
for (NodeSubscription subscription : getSubscriptions()) {
if (subscription.canSendEventNotification(node, publishedItem)) {
notifySubscriptions.add(subscription);
}
}
if (node.isMultipleSubscriptionsEnabled()) {
// Group subscriptions with the same subscriber JID
Map<JID, Collection<String>> groupedSubs = new HashMap<JID, Collection<String>>();
for (NodeSubscription subscription : notifySubscriptions) {
Collection<String> subIDs = groupedSubs.get(subscription.getJID());
if (subIDs == null) {
subIDs = new ArrayList<String>();
groupedSubs.put(subscription.getJID(), subIDs);
}
subIDs.add(subscription.getID());
}
// Send an event notification to each subscriber with a different JID
for (JID subscriberJID : groupedSubs.keySet()) {
// Get ID of affected subscriptions
Collection<String> subIDs = groupedSubs.get(subscriberJID);
// Send the notification to the subscriber
node.sendEventNotification(subscriberJID, notification, subIDs);
}
}
else {
// Affiliate should have at most one subscription so send the notification to
// the subscriber
if (!notifySubscriptions.isEmpty()) {
NodeSubscription subscription = notifySubscriptions.get(0);
node.sendEventNotification(subscription.getJID(), notification, null);
}
}
}
public String toString() {
return super.toString() + " - JID: " + getJID() + " - Affiliation: " +
getAffiliation().name();
}
/**
* Affiliation with a node defines user permissions.
*/
public static enum Affiliation {
/**
* An owner can publish, delete and purge items as well as configure and delete the node.
*/
owner,
/**
* A publisher can subscribe and publish items to the node.
*/
publisher,
/**
* A user with no affiliation can susbcribe to the node.
*/
none,
/**
* Outcast users are not allowed to subscribe to the node.
*/
outcast;
}
}
/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.pubsub;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Exception used for representing that the specified node configuration is not acceptable. A
* not acceptable error could occur if owner tries to leave the node without owners. A 406 error
* code is returned to the user that requested the invalid operation.
*
* @author Matt Tucker
*/
public class NotAcceptableException extends Exception {
private static final long serialVersionUID = 1L;
private Throwable nestedThrowable = null;
public NotAcceptableException() {
super();
}
public NotAcceptableException(String msg) {
super(msg);
}
public NotAcceptableException(Throwable nestedThrowable) {
this.nestedThrowable = nestedThrowable;
}
public NotAcceptableException(String msg, Throwable nestedThrowable) {
super(msg);
this.nestedThrowable = nestedThrowable;
}
public void printStackTrace() {
super.printStackTrace();
if (nestedThrowable != null) {
nestedThrowable.printStackTrace();
}
}
public void printStackTrace(PrintStream ps) {
super.printStackTrace(ps);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(ps);
}
}
public void printStackTrace(PrintWriter pw) {
super.printStackTrace(pw);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(pw);
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.pubsub.models;
import org.dom4j.Element;
import org.jivesoftware.wildfire.pubsub.Node;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
/**
* Policy that defines who is allowed to subscribe and retrieve items.
*
* @author Matt Tucker
*/
public abstract class AccessModel {
public final static AccessModel whitelist = new WhitelistAccess();
public final static AccessModel open = new OpenAccess();
public final static AccessModel authorize = new AuthorizeAccess();
public final static AccessModel presence = new PresenceAccess();
public final static AccessModel roster = new RosterAccess();
/**
* Returns the specific subclass of AccessModel as specified by the access
* model name. If an unknown name is specified then an IllegalArgumentException
* is going to be thrown.
*
* @param name the name of the subsclass.
* @return the specific subclass of AccessModel as specified by the access
* model name.
*/
public static AccessModel valueOf(String name) {
if ("open".equals(name)) {
return open;
}
else if ("whitelist".equals(name)) {
return whitelist;
}
else if ("authorize".equals(name)) {
return authorize;
}
else if ("presence".equals(name)) {
return presence;
}
else if ("roster".equals(name)) {
return roster;
}
throw new IllegalArgumentException("Unknown access model: " + name);
}
/**
* Returns the name as defined by the JEP-60 spec.
*
* @return the name as defined by the JEP-60 spec.
*/
public abstract String getName();
/**
* Returns true if the entity is allowed to subscribe to the specified node.
*
* @param node the node that the subscriber is trying to subscribe to.
* @param owner the JID of the owner of the subscription.
* @param subscriber the JID of the subscriber.
* @return true if the subscriber is allowed to subscribe to the specified node.
*/
public abstract boolean canSubscribe(Node node, JID owner, JID subscriber);
/**
* Returns true if the entity is allowed to get the node published items.
*
* @param node the node that the entity is trying to get the node's items.
* @param owner the JID of the owner of the subscription.
* @param subscriber the JID of the subscriber.
* @return true if the subscriber is allowed to get the node's published items.
*/
public abstract boolean canAccessItems(Node node, JID owner, JID subscriber);
/**
* Returns the error condition that should be returned to the subscriber when
* subscription is not allowed.
*
* @return the error condition that should be returned to the subscriber when
* subscription is not allowed.
*/
public abstract PacketError.Condition getSubsriptionError();
/**
* Returns the error element that should be returned to the subscriber as
* error detail when subscription is not allowed. The returned element is created
* each time this message is sent so it is safe to include the returned element in
* the parent element.
*
* @return the error element that should be returned to the subscriber as
* error detail when subscription is not allowed.
*/
public abstract Element getSubsriptionErrorDetail();
/**
* Returns true if the new subscription should be authorized by a node owner.
*
* @return true if the new subscription should be authorized by a node owner.
*/
public abstract boolean isAuthorizationRequired();
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<p>Implementation of Publish-Subscribe (JEP-0060).</p>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment