![JAR search and dependency download from the Maven repository](/logo.png)
org.jivesoftware.openfire.pubsub.NodeAffiliate Maven / Gradle / Ivy
/*
* Copyright (C) 2005-2008 Jive Software. 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.openfire.pubsub;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Element;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
/**
* 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;
public 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;
}
public 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 getSubscriptions() {
return node.getSubscriptions(jid);
}
/**
* Sends an event notification for the published items to the affiliate. The event
* notification may contain zero, one or many published items based on the items
* included in the original publication. If the affiliate has many subscriptions and
* many items were published then the affiliate will get a notification for each set
* of items that affected the same subscriptions.
*
* @param notification the message to sent to the subscribers. The message will be completed
* with the items to include in each notification.
* @param event the event Element included in the notification message. Passed as an
* optimization to avoid future look ups.
* @param leafNode the leaf node where the items where published.
* @param publishedItems the list of items that were published. Could be an empty list.
*/
void sendPublishedNotifications(Message notification, Element event, LeafNode leafNode,
List publishedItems) {
if (!publishedItems.isEmpty()) {
Map, List> itemsBySubs =
getItemsBySubscriptions(leafNode, publishedItems);
// Send one notification for published items that affect the same subscriptions
for (List nodeSubscriptions : itemsBySubs.keySet()) {
// Add items information
Element items = event.addElement("items");
items.addAttribute("node", getNode().getNodeID());
for (PublishedItem publishedItem : itemsBySubs.get(nodeSubscriptions)) {
// FIXME: This was added for compatibility with PEP supporting clients.
// Alternate solution needed when XEP-0163 version > 1.0 is released.
//
// If the node ID looks like a JID, replace it with the published item's node ID.
if (getNode().getNodeID().contains("@")) {
items.addAttribute("node", publishedItem.getNodeID());
}
// Add item information to the event notification
Element item = items.addElement("item");
if (leafNode.isItemRequired()) {
item.addAttribute("id", publishedItem.getID());
}
if (leafNode.isPayloadDelivered()) {
item.add(publishedItem.getPayload().createCopy());
}
// Add leaf leafNode information if affiliated leafNode and node
// where the item was published are different
if (leafNode != getNode()) {
item.addAttribute("node", leafNode.getNodeID());
}
}
// Send the event notification
sendEventNotification(notification, nodeSubscriptions);
// Remove the added items information
event.remove(items);
}
}
else {
// Filter affiliate subscriptions and only use approved and configured ones
List affectedSubscriptions = new ArrayList<>();
for (NodeSubscription subscription : getSubscriptions()) {
if (subscription.canSendPublicationEvent(leafNode, null)) {
affectedSubscriptions.add(subscription);
}
}
// Add item information to the event notification
Element items = event.addElement("items");
items.addAttribute("node", leafNode.getNodeID());
// Send the event notification
sendEventNotification(notification, affectedSubscriptions);
// Remove the added items information
event.remove(items);
}
}
/**
* Sends an event notification to the affiliate for the deleted items. The event
* notification may contain one or many published items based on the items included
* in the original publication. If the affiliate has many subscriptions and many
* items were deleted then the affiliate will get a notification for each set
* of items that affected the same subscriptions.
*
* @param notification the message to sent to the subscribers. The message will be completed
* with the items to include in each notification.
* @param event the event Element included in the notification message. Passed as an
* optimization to avoid future look ups.
* @param leafNode the leaf node where the items where deleted from.
* @param publishedItems the list of items that were deleted.
*/
void sendDeletionNotifications(Message notification, Element event, LeafNode leafNode,
List publishedItems) {
if (!publishedItems.isEmpty()) {
Map, List> itemsBySubs =
getItemsBySubscriptions(leafNode, publishedItems);
// Send one notification for published items that affect the same subscriptions
for (List nodeSubscriptions : itemsBySubs.keySet()) {
// Add items information
Element items = event.addElement("items");
items.addAttribute("node", leafNode.getNodeID());
for (PublishedItem publishedItem : itemsBySubs.get(nodeSubscriptions)) {
// Add retract information to the event notification
Element item = items.addElement("retract");
if (leafNode.isItemRequired()) {
item.addAttribute("id", publishedItem.getID());
}
}
// Send the event notification
sendEventNotification(notification, nodeSubscriptions);
// Remove the added items information
event.remove(items);
}
}
}
/**
* 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.
*
* Event notifications may include notifications of new published items or of items that
* were deleted.
*
* 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 notifySubscriptions list of subscriptions that were affected and are going to be
* included in the notification message. The list should not be empty.
*/
private void sendEventNotification(Message notification,
List notifySubscriptions) {
if (node.isMultipleSubscriptionsEnabled()) {
// Group subscriptions with the same subscriber JID
Map> groupedSubs = new HashMap<>();
for (NodeSubscription subscription : notifySubscriptions) {
Collection subIDs = groupedSubs.get(subscription.getJID());
if (subIDs == null) {
subIDs = new ArrayList<>();
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 subIDs = groupedSubs.get(subscriberJID);
// Send the notification to the subscriber
node.sendEventNotification(subscriberJID, notification, subIDs);
}
}
else {
// Affiliate should have at most one subscription per unique JID
if (!notifySubscriptions.isEmpty()) {
List subs = new ArrayList<>();
for(NodeSubscription subscription: notifySubscriptions) {
JID sub = subscription.getJID();
if (!subs.contains(sub)) {
node.sendEventNotification(sub, notification, null);
subs.add(sub);
}
}
}
}
}
private Map, List> getItemsBySubscriptions(
LeafNode leafNode, List publishedItems) {
// Identify which subscriptions can receive each item
Map> subsByItem =
new HashMap<>();
// Filter affiliate subscriptions and only use approved and configured ones
Collection subscriptions = getSubscriptions();
for (PublishedItem publishedItem : publishedItems) {
for (NodeSubscription subscription : subscriptions) {
if (subscription.canSendPublicationEvent(leafNode, publishedItem)) {
List nodeSubscriptions = subsByItem.get(publishedItem);
if (nodeSubscriptions == null) {
nodeSubscriptions = new ArrayList<>();
subsByItem.put(publishedItem, nodeSubscriptions);
}
nodeSubscriptions.add(subscription);
}
}
}
// Identify which items should be sent together to the same subscriptions
Map, List> itemsBySubs =
new HashMap<>();
List affectedSubscriptions;
for (PublishedItem publishedItem : subsByItem.keySet()) {
affectedSubscriptions = itemsBySubs.get(subsByItem.get(publishedItem));
if (affectedSubscriptions == null) {
List items = new ArrayList<>(publishedItems.size());
items.add(publishedItem);
itemsBySubs.put(subsByItem.get(publishedItem), items);
}
else {
affectedSubscriptions.add(publishedItem);
}
}
return itemsBySubs;
}
@Override
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
}
}