/*
* 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);
}
}