/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.vysper.xmpp.modules.extension.xep0060_pubsub.model; import java.util.ArrayList; import java.util.List; import org.apache.vysper.xml.fragment.XMLElement; import org.apache.vysper.xmpp.addressing.Entity; import org.apache.vysper.xmpp.delivery.StanzaRelay; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.ItemVisitor; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.MemberAffiliationVisitor; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.PubSubAffiliation; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.PubSubServiceConfiguration; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.SubscriberPayloadNotificationVisitor; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.SubscriberVisitor; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.feature.PubsubFeatures; import org.apache.vysper.xmpp.modules.extension.xep0060_pubsub.storageprovider.LeafNodeStorageProvider; import org.apache.vysper.xmpp.modules.servicediscovery.management.Feature; import org.apache.vysper.xmpp.modules.servicediscovery.management.Identity; import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoElement; import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoRequest; import org.apache.vysper.xmpp.protocol.NamespaceURIs; /** * This class is the model for leaf nodes. Leaf nodes contain messages and subscribers in various * forms. * * @author The Apache MINA Project (http://mina.apache.org) */ public class LeafNode { // the name of the node (free text) protected String title; // the unique name (per server) of the node protected String name; // the storage provider for storing and retrieving node information. protected LeafNodeStorageProvider storage = null; // the service configuration protected PubSubServiceConfiguration serviceConfiguration = null; /** * Creates a new LeafNode with the specified name and title. The creator will be added as owner. */ public LeafNode(PubSubServiceConfiguration serviceConfiguration, String name, String title, Entity creator) { init(serviceConfiguration, name, title, creator); } /** * Creates a new LeafNode with the specified name. The creator will be added as owner. */ public LeafNode(PubSubServiceConfiguration serviceConfiguration, String name, Entity creator) { init(serviceConfiguration, name, null, creator); } /** * Method to actually do the initialization process. * * @param serviceConfiguration * @param name * @param title * @param creator */ private void init(PubSubServiceConfiguration serviceConfiguration, String name, String title, Entity creator) { this.serviceConfiguration = serviceConfiguration; this.name = name; this.title = title; this.storage = serviceConfiguration.getLeafNodeStorageProvider(); this.storage.initialize(this); try { this.setAffiliation(creator, PubSubAffiliation.OWNER); } catch (LastOwnerResignedException e) { // won't happen } } /** * Changes the persistenceManager. * @param persistenceManager the new persistence manager. */ public void setPersistenceManager(LeafNodeStorageProvider persistenceManager) { this.storage = persistenceManager; } /** * Add a new subscriber with the given id. * @param id subscription ID * @param subscriber the subscriber */ public void subscribe(String id, Entity subscriber) { storage.addSubscriber(name, id, subscriber); } /** * Check whether a JID is already subscribed * @param subscriber the JID to check * @return true if the JID is already subscribed */ public boolean isSubscribed(Entity subscriber) { return storage.containsSubscriber(name, subscriber); } /** * Check whether if we already have a subscription with the given ID * @param subscriptionID the ID to check for. * @return true if a subscription with this ID is present. */ public boolean isSubscribed(String subscriptionID) { return storage.containsSubscriber(name, subscriptionID); } /** * Remove a subscription of a JID with a given subscription ID. * @param subscriptionID the subscription ID of the JID. * @param subscriber the JID of the subscriber. * @return true if the subscription has been removed, false otherwise. */ public boolean unsubscribe(String subscriptionID, Entity subscriber) { Entity sub = storage.getSubscriber(name, subscriptionID); if (sub != null && sub.equals(subscriber)) { return storage.removeSubscription(name, subscriptionID); } return false; } /** * Remove a subscription solely with the subscription JID, if more than one subscription * with this JID is present an exception will be thrown. * * @param subscriber the JID to unsubscribe. * @return true if the subscription has been removed. * @throws MultipleSubscriptionException if more than one subscription with this JID is present. */ public boolean unsubscribe(Entity subscriber) throws MultipleSubscriptionException { if (countSubscriptions(subscriber) > 1) { throw new MultipleSubscriptionException("Ambigous unsubscription request"); } return storage.removeSubscriber(name, subscriber); } /** * Returns the number of subscriptions with the given JID. * @param subscriber the JID to count. * @return number of subscriptions. */ public int countSubscriptions(Entity subscriber) { return storage.countSubscriptions(name, subscriber); } /** * Returns the total number of subscriptions (based on the subscriptions IDs, not the JIDs). * @return number of subscriptions. */ public int countSubscriptions() { return storage.countSubscriptions(name); } /** * Publish an item to this node. * @param sender the sender of the message (publisher). * @param relay the relay for sending the messages. * @param itemID the ID of the published message. * @param item the payload of the message. */ public void publish(Entity sender, StanzaRelay relay, String itemID, XMLElement item) { storage.addMessage(sender, name, itemID, item); sendMessageToSubscriber(relay, item); } /** * Sends a message to each subscriber of the node. * * @param stanzaRelay the relay for sending the notifications. * @param item the payload of the message. */ protected void sendMessageToSubscriber(StanzaRelay stanzaRelay, XMLElement item) { storage.acceptForEachSubscriber(name, new SubscriberPayloadNotificationVisitor(serviceConfiguration .getDomainJID(), stanzaRelay, item)); } /** * Call the SubscriberVisitor for each subscription of this node. * @param sv */ public void acceptSubscribers(SubscriberVisitor sv) { storage.acceptForEachSubscriber(name, sv); } /** * @return the name of the node. */ public String getName() { return name; } /** * @return the free-text title of the node */ public String getTitle() { return title; } /** * Builds a list of InfoElements for disco#info requests. * * @param request the sent request * @return the list of InfoElements */ public List<InfoElement> getNodeInfosFor(InfoRequest request) { List<InfoElement> infoElements = new ArrayList<InfoElement>(); infoElements.add(new Identity("pubsub", "leaf")); infoElements.add(new Feature(NamespaceURIs.XEP0060_PUBSUB)); infoElements.add(PubsubFeatures.ACCESS_OPEN.getFeature()); infoElements.add(PubsubFeatures.ITEM_IDS.getFeature()); infoElements.add(PubsubFeatures.PERSISTENT_ITEMS.getFeature()); infoElements.add(PubsubFeatures.MULTI_SUBSCRIBE.getFeature()); infoElements.add(PubsubFeatures.PUBLISH.getFeature()); infoElements.add(PubsubFeatures.SUBSCRIBE.getFeature()); infoElements.add(PubsubFeatures.RETRIEVE_SUBSCRIPTIONS.getFeature()); infoElements.add(PubsubFeatures.RETRIEVE_AFFILIATIONS.getFeature()); infoElements.add(PubsubFeatures.MODIFY_AFFILIATIONS.getFeature()); return infoElements; } /** * Visits each item ever published to this node. * * @param iv the visitor */ public void acceptItems(ItemVisitor iv) { storage.acceptForEachItem(name, iv); } /** * Visits each member of this node. * * @param mav the visitor */ public void acceptMemberAffiliations(MemberAffiliationVisitor mav) { storage.acceptForEachMemberAffiliation(name, mav); } /** * Called after all information, including the persistencemanager for the * node is set. */ public void initialize() { storage.initialize(this); } /** * Removes this node from the storage. */ public void delete() { this.storage.delete(this.name); } /** * Adds the given entity to the owner list. * * @param owner */ public void setAffiliation(Entity owner, PubSubAffiliation affiliation) throws LastOwnerResignedException { this.storage.setAffiliation(name, owner, affiliation); } /** * Check whether the given JID is allowed to perform the requested task. * @param sender * @param requestedAffiliation * @return */ public boolean isAuthorized(Entity sender, PubSubAffiliation requestedAffiliation) { PubSubAffiliation affiliation = this.storage.getAffiliation(name, sender); return affiliation.compareTo(requestedAffiliation) >= 0; } /** * Returns the affiliation for the given bareJID. * @param entity the entity for which the affiliation should be returned. * @return All affiliations ("NONE" if no other affiliation is known). */ public PubSubAffiliation getAffiliation(Entity entity) { return this.storage.getAffiliation(name, entity); } }