Package org.apache.muse.ws.notification.impl

Source Code of org.apache.muse.ws.notification.impl.SimpleNotificationProducer

/*=============================================================================*
*  Copyright 2006 The Apache Software Foundation
*
*  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.apache.muse.ws.notification.impl;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.apache.muse.core.Persistence;
import org.apache.muse.core.Resource;
import org.apache.muse.core.ResourceManager;
import org.apache.muse.core.ResourceManagerListener;
import org.apache.muse.core.routing.MessageHandler;
import org.apache.muse.util.LoggingUtils;
import org.apache.muse.util.ReflectUtils;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.xml.XmlSerializable;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.addressing.EndpointReference;
import org.apache.muse.ws.resource.basefaults.BaseFault;
import org.apache.muse.ws.notification.*;
import org.apache.muse.ws.notification.faults.*;
import org.apache.muse.ws.notification.properties.*;
import org.apache.muse.ws.notification.topics.*;
import org.apache.muse.ws.notification.topics.impl.*;
import org.apache.muse.ws.resource.WsResource;
import org.apache.muse.ws.resource.impl.AbstractWsResourceCapability;
import org.apache.muse.ws.resource.lifetime.ScheduledTermination;
import org.apache.muse.ws.resource.lifetime.WsrlConstants;
import org.apache.muse.ws.resource.properties.ResourcePropertyCollection;
import org.apache.muse.ws.resource.properties.WsrpConstants;
import org.apache.muse.ws.resource.properties.listeners.PropertyChangeListener;
import org.apache.muse.ws.addressing.soap.SoapFault;

/**
*
* SimpleNotificationProducer is Muse's default implementation of the
* WS-Notification NotificationProducer port type.
* <br><br>
* Resources that implement this port type should be deployed with the
* {@linkplain SubscriptionManager SubscriptionManager} type as well; the
* subscription resources cannot be managed without this endpoint.
*
* @author Dan Jemiolo (danj)
*
*/

public class SimpleNotificationProducer
    extends AbstractWsResourceCapability implements NotificationProducer, ResourceManagerListener
{
    //
    // Used to lookup all exception messages
    //
    private static Messages _MESSAGES = MessagesFactory.get(SimpleNotificationProducer.class);
   
    private static final String _TOPIC_SET_PARAM = "topic-set";
   
    //
    // all topic names in the topic set - not a hierarchical collection
    //
    private Set _allTopicNames = new HashSet();
   
    //
    // factory that will provide WSRP notifications with the right wrapper
    //
    private ChangeNotificationListenerFactory _listenerFactory = null;
   
    //
    // context path of the subscription manager resource type
    //
    private String _subscriptionPath = null;
   
    //
    // EPR -> current subscriptions
    //
    private Map _subscriptionsByEPR = new HashMap();
   
    //
    // hierarchical topic set supported by the resource
    //
    private TopicSet _topicSet = new SimpleTopicSet();
   
    public synchronized void addSubscription(WsResource sub)
    {
        SubscriptionManager subMgr =
            (SubscriptionManager)sub.getCapability(WsnConstants.SUBSCRIPTION_MGR_URI);
        EndpointReference epr = sub.getEndpointReference();
        _subscriptionsByEPR.put(epr, subMgr);
    }
   
    public Topic addTopic(QName topicName)
        throws BaseFault
    {
        String namespace = topicName.getNamespaceURI();
        TopicNamespace topicSpace = getTopicNamespace(namespace);
       
        //
        // make sure the property's namespace has a topic space
        //
        if (topicSpace == null)
            topicSpace = addTopicNamespace(namespace);
       
        //
        // if the property is new, we need to add a topic for it so
        // clients can subscribe to change events
        //
        String localName = topicName.getLocalPart();
        Topic topic = new SimpleTopic(localName, topicSpace);
        topicSpace.addTopic(topic);
       
        //
        // add to our collection of names as well as the topic set tree
        //
        _allTopicNames.add(topicName);
       
        return topic;
    }
   
    public TopicNamespace addTopicNamespace(String namespace)
        throws BaseFault
    {
        TopicNamespace topics = new SimpleTopicNamespace(namespace);
        topics.setName(WsrpConstants.TOPIC_SPACE_NAME);
       
        _topicSet.addTopicNamespace(topics);
       
        return topics;
    }
   
    protected void addTopics(Element topicTree,
                             String namespace,
                             String prefix)
        throws BaseFault
    {
        String name = topicTree.getLocalName();
        addTopic(new QName(namespace, name, prefix));
       
        Element[] children = XmlUtils.getAllElements(topicTree);
       
        for (int n = 0; n < children.length; ++n)
            addTopics(children[n], namespace, prefix);
    }
   
    /**
     *
     * Users can override this method to provide an alternative implementation
     * of ChangeNotificationListenerFactory that wraps WS-RP change notifications
     * in a different set of XML content.
     *
     * @return An instance of WsrpNotificationListenerFactory
     *
     */
    protected ChangeNotificationListenerFactory createNotificationListenerFactory()
    {
        return new WsrpNotificationListenerFactory();
    }
   
    /**
     *
     * Users can override this method to provide an alternative implementation
     * of NotificationMessage.
     *
     * @return An instance of SimpleNotificationMessage
     *
     */
    protected NotificationMessage createNotificationMessage()
    {
        return new SimpleNotificationMessage();
    }
   
    protected MessageHandler createSubscribeHandler()
    {
        MessageHandler handler = new SubscribeHandler(getWsResource());
       
        Method method = ReflectUtils.getFirstMethod(getClass(), "subscribe");
        handler.setMethod(method);
       
        return handler;
    }
   
    protected void createTopicSetDocument(String topicSetFile)
        throws BaseFault
    {
        Document doc = getEnvironment().getDocument(topicSetFile);
        Element topicSetXML = XmlUtils.getFirstElement(doc);
        Element[] topicTrees = XmlUtils.getAllElements(topicSetXML);
       
        for (int n = 0; n < topicTrees.length; ++n)
        {
            String uri = topicTrees[n].getNamespaceURI();
            String prefix = topicTrees[n].getPrefix();
            addTopics(topicTrees[n], uri, prefix);
        }
    }
   
    public NotificationMessage getCurrentMessage(QName topicPath)
        throws NoCurrentMessageOnTopicFault,
               TopicNotSupportedFault
    {
        Topic topic = getTopic(topicPath);
       
        if (topic == null)
        {
            Object[] filler = { topicPath };
            throw new TopicNotSupportedFault(_MESSAGES.get("TopicNotFound", filler));
        }
       
        NotificationMessage message = topic.getCurrentMessage();
       
        if (message == null)
        {
            Object[] filler = { topicPath };
            throw new NoCurrentMessageOnTopicFault(_MESSAGES.get("NoMessageAvailable", filler));
        }
       
        return message;
    }
   
    public boolean getFixedTopicSet()
    {
        return true;
    }
   
    protected ChangeNotificationListenerFactory getNotificationListenerFactory()
    {
        return _listenerFactory;
    }
   
    public QName[] getPropertyNames()
    {
        return PROPERTIES;
    }
   
    protected String getSubscriptionContextPath()
    {
        return _subscriptionPath;
    }
   
    protected synchronized Collection getSubscriptions()
    {
        return Collections.unmodifiableCollection(_subscriptionsByEPR.values());
    }
   
    public Topic getTopic(QName topicName)
    {
        TopicNamespace topicSpace = getTopicNamespace(topicName.getNamespaceURI());
       
        if (topicSpace == null)
            return null;
       
        return topicSpace.getTopic(topicName.getLocalPart());
    }
   
    public QName[] getTopicExpression()
    {
        QName[] array = new QName[_allTopicNames.size()];
        return (QName[])_allTopicNames.toArray(array);
    }
   
    public String[] getTopicExpressionDialect()
    {
        return new String[]{ WstConstants.CONCRETE_TOPIC_URI };
    }
   
    public TopicNamespace getTopicNamespace(String namespace)
    {
        return getTopicSet().getTopicNamespace(namespace);
    }

    public TopicSet getTopicSet()
    {
        return _topicSet;
    }

    public boolean hasTopic(QName topicName)
    {
        return _allTopicNames.contains(topicName);
    }
   
    public void initialize()
        throws SoapFault
    {
        super.initialize();
       
        _listenerFactory = createNotificationListenerFactory();
       
        setMessageHandler(createSubscribeHandler());
       
        //
        // make sure we're exposing the subscription endpoint so that
        // clients can manage subscriptions/consumers
        //
        WsResource resource = getWsResource();
        ResourceManager manager = resource.getResourceManager();
        _subscriptionPath = manager.getResourceContextPath(SubscriptionManager.class);

        if (_subscriptionPath == null)
            throw new RuntimeException(_MESSAGES.get("NoSubscriptionManager"));
       
        //
        // make sure any persistence impl is of the right type
        //
        Persistence persistence = getPersistence();
       
        if (persistence != null)
        {
            if (!NotificationProducerPersistence.class.isAssignableFrom(persistence.getClass()))
            {
                Object[] filler = { NotificationProducerPersistence.class, persistence.getClass() };
                throw new RuntimeException(_MESSAGES.get("IncorrectPersistenceRoot", filler));
            }
           
            NotificationProducerPersistence npPersistence = (NotificationProducerPersistence)persistence;
            npPersistence.setNotificationProducer(this);
        }
       
        //
        // make sure we can listen for new subscriptions/destructions
        //
        manager.addListener(this);
       
        //
        // if there are any user-defined topic spaces, find those files
        // and make sure they exist
        //
        String topicSetFile = getInitializationParameter(_TOPIC_SET_PARAM);
       
        if (topicSetFile != null)
            createTopicSetDocument(topicSetFile);
    }
   
    public void initializeCompleted()
        throws SoapFault
    {
        super.initializeCompleted();
       
        ChangeNotificationListenerFactory factory = getNotificationListenerFactory();
       
        //
        // for every property in the resource's ws-rp doc, create a property
        // change listener that will send out notifications for each change
        //
        WsResource resource = getWsResource();
        ResourcePropertyCollection props = resource.getPropertyCollection();
        Iterator i = props.getPropertyNames().iterator();
       
        while (i.hasNext())
        {
            QName name = (QName)i.next();
           
            addTopic(name);
            _allTopicNames.add(name);
           
            PropertyChangeListener listener = factory.newInstance(name, resource);
            props.addChangeListener(listener);
        }
       
        //
        // if the resource supports either WS-RL capability, add support
        // for the WS-RL termination topic
        //
        if (resource.hasCapability(WsrlConstants.IMMEDIATE_TERMINATION_URI) ||
            resource.hasCapability(WsrlConstants.SCHEDULED_TERMINATION_URI))
        {
            addTopic(WsrlConstants.TERMINATION_TOPIC_QNAME);
            _allTopicNames.add(WsrlConstants.TERMINATION_TOPIC_QNAME);
        }
    }
   
    public void prepareShutdown()
        throws SoapFault
    {
        //
        // if WSRL is present, send a termination notification to subscribers
        // before all subscriptions are destroyed
        //
        if (hasTopic(WsrlConstants.TERMINATION_TOPIC_QNAME))
        {
            Element payload = XmlUtils.createElement(WsrlConstants.NOTIFICATION_QNAME);
            XmlUtils.setElement(payload, WsrlConstants.TERMINATION_TIME_QNAME, new Date());
           
            publish(WsrlConstants.TERMINATION_TOPIC_QNAME, payload);
        }
       
        super.prepareShutdown();
    }
   
    public void publish(QName topicName, Element content)
        throws SoapFault
    {
        //
        // construct the message/payload
        //
        NotificationMessage message = createNotificationMessage();
        message.addMessageContent(content);
       
        message.setTopic(topicName);
        message.setProducerReference(getResource().getEndpointReference());
       
        //
        // give the message to each subscription so it can decide if it
        // should be sent or not
        //
        Iterator i = getSubscriptions().iterator();
       
        while (i.hasNext())
        {
            SubscriptionManager sub = (SubscriptionManager)i.next();
            sub.publish(message);
        }
       
        //
        // if a topic was used, record the message as the 'current' message
        // for the topic (will be returned by getCurrentMessage())
        //
        if (topicName != null)
        {
            Topic topic = getTopic(topicName);
            topic.setCurrentMessage(message);
        }
    }
   
    public void publish(QName topicName, XmlSerializable content)
        throws SoapFault
    {
        publish(topicName, content.toXML());
    }
   
    public synchronized void removeSubscription(EndpointReference epr)
    {
        _subscriptionsByEPR.remove(epr);
    }

    public void resourceAdded(EndpointReference epr, Resource resource)
    {
        if (resource.hasCapability(WsnConstants.SUBSCRIPTION_MGR_URI))
        {
            addSubscription((WsResource)resource);
       
            NotificationProducerPersistence persistence = (NotificationProducerPersistence)getPersistence();
           
            try
            {
                if (persistence != null)
                    persistence.resourceAdded(epr, resource);
            }
           
            catch (SoapFault fault)
            {
                LoggingUtils.logError(getLog(), fault);
            }
        }
    }

    public void resourceRemoved(EndpointReference epr)
    {
        //
        // this call is synchronized, so if we're publishing, the
        // collection will not be updated until afterwards
        //
        removeSubscription(epr);
       
        NotificationProducerPersistence persistence = (NotificationProducerPersistence)getPersistence();
       
        try
        {
            if (persistence != null)
                persistence.resourceRemoved(epr);
        }
       
        catch (SoapFault fault)
        {
            LoggingUtils.logError(getLog(), fault);
        }
    }

    public WsResource subscribe(EndpointReference consumer,
                                Filter filter,
                                Date terminationTime,
                                Policy policy)
        throws TopicNotSupportedFault,
               UnacceptableInitialTerminationTimeFault,
               SubscribeCreationFailedFault
    {
        if (consumer == null)
            throw new NullPointerException(_MESSAGES.get("NullConsumerEPR"));
       
        //
        // sanity check - the message handler should do this, but we handle
        // null filters just in case
        //
        if (filter == null)
            filter = PublishAllMessagesFilter.getInstance();
       
        //
        // HACK: ugh. topic filters are different from message
        //       pattern and properties filters because they
        //       rely on internal data structures (topics) that
        //       are more concretely defined than the other two
        //       filter types. it's a one-off. we have to check
        //       against the topic set of the resource, and there
        //       is no such equivalent task for other filter types.
        //       so we downcast.
        //
        if (filter instanceof TopicFilter)
        {
            TopicFilter topicFilter = (TopicFilter)filter;
            QName topic = topicFilter.getTopic();
           
            if (!hasTopic(topic))
                throw new TopicNotSupportedFault("Topic not found: " + topic);
        }
       
        WsResource producer = getWsResource();
        ResourceManager manager = producer.getResourceManager();
       
        //
        // create the resource to represent the subscription
        //
        String endpoint = getSubscriptionContextPath();
        WsResource sub = null;
       
        try
        {
            sub = (WsResource)manager.createResource(endpoint);
        }
       
        catch (SoapFault error)
        {
            throw new SubscribeCreationFailedFault(error);
        }
       
        //
        // use the subscription capability to set all the subscription fields
        //
        SubscriptionManager subMgr =
            (SubscriptionManager)sub.getCapability(WsnConstants.SUBSCRIPTION_MGR_URI);
       
        EndpointReference producerEPR = producer.getEndpointReference();
        subMgr.setProducerReference(producerEPR);
       
        subMgr.setConsumerReference(consumer);
        subMgr.setFilter(filter);
        subMgr.setSubscriptionPolicy(policy);
       
        try
        {
            sub.initialize();
            manager.addResource(sub.getEndpointReference(), sub);
        }
       
        catch (SoapFault error)
        {
            throw new SubscribeCreationFailedFault(error);           
        }
       
        //
        // set termination time AFTER we initialize so that we can rely
        // on the base Resource class' implementation of WS-RL (which
        // tries to apply the change immediately, rather than storing it
        // in a field and setting it during initialization)
        //
        if (sub.hasCapability(WsrlConstants.SCHEDULED_TERMINATION_URI))
        {
            ScheduledTermination wsrl = (ScheduledTermination)sub.getCapability(WsrlConstants.SCHEDULED_TERMINATION_URI);
           
            try
            {
                wsrl.setTerminationTime(terminationTime);
            }
           
            catch (BaseFault error)
            {
                throw new UnacceptableInitialTerminationTimeFault(error);
            }
        }
       
        return sub;
    }
}
TOP

Related Classes of org.apache.muse.ws.notification.impl.SimpleNotificationProducer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.