Package org.apache.ws.notification.base.impl

Source Code of org.apache.ws.notification.base.impl.NotificationProducerHelper

/*=============================================================================*
*  Copyright 2004 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.ws.notification.base.impl;

import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.Soap1_1Constants;
import org.apache.ws.addressing.EndpointReference;
import org.apache.ws.addressing.XmlBeansEndpointReference;
import org.apache.ws.addressing.v2003_03.AddressingConstants;
import org.apache.ws.notification.base.NotificationProducerResource;
import org.apache.ws.notification.base.Subscription;
import org.apache.ws.notification.base.SubscriptionManager;
import org.apache.ws.notification.base.v2004_06.BaseNotificationConstants;
import org.apache.ws.notification.base.v2004_06.impl.SubscriptionHome;
import org.apache.ws.notification.base.v2004_06.porttype.NotificationProducerPortType;
import org.apache.ws.notification.topics.Topic;
import org.apache.ws.notification.topics.TopicsTypeWriter;
import org.apache.ws.notification.topics.expression.InvalidTopicExpressionException;
import org.apache.ws.notification.topics.expression.TopicExpression;
import org.apache.ws.notification.topics.expression.TopicExpressionException;
import org.apache.ws.notification.topics.expression.TopicPathDialectUnknownException;
import org.apache.ws.notification.topics.v2004_06.TopicsConstants;
import org.apache.ws.pubsub.emitter.EmitterTask;
import org.apache.ws.resource.faults.FaultException;
import org.apache.ws.resource.properties.ResourcePropertySet;
import org.apache.ws.resource.properties.query.InvalidQueryExpressionException;
import org.apache.ws.resource.properties.query.QueryEngine;
import org.apache.ws.resource.properties.query.QueryEvaluationErrorException;
import org.apache.ws.resource.properties.query.QueryExpression;
import org.apache.ws.resource.properties.query.UnknownQueryExpressionDialectException;
import org.apache.ws.resource.properties.query.impl.QueryEngineImpl;
import org.apache.ws.util.JaxpUtils;
import org.apache.ws.util.XmlBeanUtils;
import org.apache.ws.util.thread.NamedThread;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.oasisOpen.docs.wsn.x2004.x06.wsnWSBaseNotification12Draft01.NotificationMessageHolderType;
import org.oasisOpen.docs.wsn.x2004.x06.wsnWSBaseNotification12Draft01.NotifyDocument;
import org.oasisOpen.docs.wsn.x2004.x06.wsnWSBaseNotification12Draft01.TopicExpressionType;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xmlsoap.schemas.ws.x2003.x03.addressing.EndpointReferenceType;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import java.io.IOException;
import java.net.URL;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* A helper class for a notification producer resource that keeps track of subscriptions and publishes messages.
* Note, this class is specification version neutral.
*
* @author Ian Springer
*/
public class NotificationProducerHelper
{
   private static Log                  LOG          =
      LogFactory.getLog( NotificationProducerHelper.class.getName(  ) );
   private static final QueryEngine    QUERY_ENGINE = new QueryEngineImpl(  );
   private static final PooledExecutor EMITTER_POOL;

   static
   {
      EMITTER_POOL = new PooledExecutor( 100 );

      // make sure the threads are non-daemon threads so they have time to complete even if the JVM wants to shut down
      EMITTER_POOL.setThreadFactory( new NamedThread.ConcurrentThreadFactory( "notifmgr-emitter", false ) );
   }

   private NotificationProducerResource m_producerResource;

   /*
    * Maps {@link Topic}s to {@link Object}s respresenting the most recent messages
    * published to those Topics.
    */
   private Map m_currentMsgMap = Collections.synchronizedMap( new HashMap(  ) );

   /**
    * Creates a new {@link NotificationProducerHelper} object.
    *
    * @param producerResource DOCUMENT_ME
    */
   public NotificationProducerHelper( NotificationProducerResource producerResource )
   {
      m_producerResource = producerResource;
   }

   /**
    *
    * @param topicExpr a topic expression describing EXACTLY ONE topic
    *
    * @return the current message
    */
   public Object getCurrentMessage( TopicExpression topicExpr )
   throws InvalidTopicExpressionException,
          TopicNotSupportedException,
          NoCurrentMessageOnTopicException
   {
      Topic  topic      = toTopic( topicExpr );
      Object currentMsg = m_currentMsgMap.get( topic );
      if ( currentMsg == null )
      {
         throw new NoCurrentMessageOnTopicException( topic );
      }

      return currentMsg;
   }

   /**
    * Publishes the given message to the topic identified by the given topic expression.
    *
    * @param topicExpr a topic expression describing EXACTLY ONE topic
    * @param msg       the notification message to be published - may be a {@link org.w3c.dom.Node} or an {@link
    *                  org.apache.xmlbeans.XmlObject}
    */
   public void publish( TopicExpression topicExpr,
                        Object          msg )
   throws TopicNotSupportedException,
          InvalidTopicExpressionException,
          MessageTypeNotSupportedException
   {
      Topic topic = toTopic( topicExpr );
      m_currentMsgMap.put( topic, msg );
      Subscription[] subscriptions =
         SubscriptionManager.getInstance(  ).getSubscriptions( m_producerResource, topic );
      LOG.debug( "Topic " + topic + " matched " + subscriptions.length + " subscriptions." );
      XmlObject msgXBean;
      try
      {
         msgXBean = XmlBeanUtils.toXmlObject( msg );
      }
      catch ( Exception e )
      {
         throw new MessageTypeNotSupportedException( "Unable to convert " + msg.getClass(  ).getName(  )
                                                     + " object into an XmlObject.", e );
      }

      for ( int i = 0; i < subscriptions.length; i++ )
      {
         Subscription subscription = subscriptions[i];
         try
         {
            notify( subscription, topicExpr, msgXBean );
         }
         catch ( Exception e )
         {
            e.printStackTrace(  );
         }
      }
   }

   /**
    * Subscribe to the topic(s) indicated by the specified topic expression.
    * Notifications for the subscription will be sent to the specified
    * consumer EPR. Other optional parameters may also be specified.
    *
    * @param consumerEPR
    * @param topicExpr
    * @param useNotify
    * @param precondition
    * @param selector
    * @param policy
    * @param initialTerminationTime
    *
    * @return the EPR of the newly created subscription
    */
   public EndpointReference subscribe( EndpointReference consumerEPR,
                                       TopicExpression   topicExpr,
                                       Boolean           useNotify,
                                       QueryExpression   precondition,
                                       QueryExpression   selector,
                                       Object            policy,
                                       Calendar          initialTerminationTime )
   throws SubscribeCreationFailedException,
          InvalidTopicExpressionException,
          TopicPathDialectUnknownException
   {
      if ( consumerEPR == null )
      {
         throw new IllegalArgumentException( "The consumer EPR parameter may not be null." );
      }

      if ( topicExpr == null )
      {
         throw new IllegalArgumentException( "The topic expression parameter may not be null." );
      }

      Topic[] topics = evaluateTopicExpression( topicExpr );
      if ( topics.length == 0 )
      {
         throw new InvalidTopicExpressionException( "Given TopicExpression did not match any Topics supported by this NotificationProducer - the WS-BaseN spec mandates that it match at least one." );
      }

      // TODO: SubscriptionHome class is specific to WSN 2004/06 - should be replaced by a WSN-version-neutral SubscriptionHome interface
      SubscriptionHome subscriptionHome =
         (SubscriptionHome) SubscriptionManager.getInstance(  ).getSubscriptionManagerHome( m_producerResource );
      Subscription     subscription = null;
      try
      {
         subscription =
            subscriptionHome.create( m_producerResource.getEndpointReference(  ),
                                     consumerEPR,
                                     topicExpr );
         subscription.setTerminationTime( initialTerminationTime );
         subscription.setUseNotify( ( useNotify == null ) || useNotify.booleanValue(  ) );
         subscription.setSelector( selector );
         subscription.setPrecondition( precondition );
         subscription.setPolicy( policy );
         SubscriptionManager.getInstance(  ).addSubscription( subscription, topics );
      }
      catch ( Exception e )
      {
         LOG.error( "Subscribe failed due to internal error: " + e );
         if ( LOG.isDebugEnabled(  ) )
         {
            e.printStackTrace(  );
         }

         throw new SubscribeCreationFailedException( e );
      }

      return subscription.getEndpointReference(  );
   }

   private void addMessageAddressingPropertiesToHeader( Object[]   props,
                                                        SOAPHeader header )
   throws Exception
   {
      if ( props != null )
      {
         for ( int i = 0; i < props.length; i++ )
         {
            XmlObject refProp = (XmlObject) props[i];

            // TODO: *SJC* ideally, the below logic would handle refParams that are complexTypes...
            // TODO (ips): see addSOAPBodyElements() in ResourceHandler for a fairly easy way to do it
            SOAPElement       soapElem   = XmlBeanUtils.toSOAPElement( refProp );
            SOAPHeaderElement headerElem = header.addHeaderElement( soapElem.getElementName(  ) );
            headerElem.addTextNode( soapElem.getValue(  ) );
         }
      }
   }

   private void addReferenceParametersToHeader( SOAPHeader        header,
                                                EndpointReference consumerEPR )
   throws Exception
   {
      addMessageAddressingPropertiesToHeader( consumerEPR.getReferenceParameters(  ),
                                              header );
   }

   private void addReferencePropertiestoHeader( SOAPHeader        header,
                                                EndpointReference consumerEPR )
   throws Exception
   {
      addMessageAddressingPropertiesToHeader( consumerEPR.getReferenceProperties(  ),
                                              header );
   }

   /*
    * Add WS-Addressing headers to a notification.
    *
    * @param header the header to which to add the WS-Addressing headers.
    * @param consumerEPR the EPR to the consumer of this notification.
    * @throws Exception
    */
   private void addWSAHeaders( SOAPHeader        header,
                               EndpointReference consumerEPR )
   throws Exception
   {
      SOAPFactory factory = SOAPFactory.newInstance(  );

      // TODO: *SJC* this should not be hard-coded to use WSA 2003/03. Once a new version of WSN is implemented we will need to support multiple versions
      SOAPHeaderElement headerElem =
         header.addHeaderElement( factory.createName( org.apache.ws.addressing.v2003_03.AddressingConstants.TO,
                                                      org.apache.ws.addressing.v2003_03.AddressingConstants.NSPREFIX_ADDRESSING_SCHEMA,
                                                      org.apache.ws.addressing.v2003_03.AddressingConstants.NSURI_ADDRESSING_SCHEMA ) );
      headerElem.addTextNode( consumerEPR.getAddress(  ) );
      headerElem =
         header.addHeaderElement( factory.createName( AddressingConstants.ACTION,
                                                      AddressingConstants.NSPREFIX_ADDRESSING_SCHEMA,
                                                      AddressingConstants.NSURI_ADDRESSING_SCHEMA ) );
      headerElem.addTextNode( BaseNotificationConstants.NOTIFY_ACTION_URI );
      addReferencePropertiestoHeader( header, consumerEPR );
      addReferenceParametersToHeader( header, consumerEPR );
   }

   private SOAPMessage buildSOAPMessage( Document          fullMsgBodyElem,
                                         EndpointReference consumerEPR )
   throws Exception
   {
      SOAPMessage  msg      = MessageFactory.newInstance(  ).createMessage(  );
      SOAPEnvelope envelope = msg.getSOAPPart(  ).getEnvelope(  );
      SOAPBody     body     = envelope.getBody(  );
      body.addDocument( fullMsgBodyElem );
      SOAPHeader header = msg.getSOAPHeader(  );
      addWSAHeaders( header, consumerEPR );
      return msg;
   }

   private boolean evaluatePrecondition( QueryExpression     precondition,
                                         ResourcePropertySet propSet )
   throws Exception
   {
      boolean result;
      if ( precondition == null )
      {
         result = true;
      }
      else
      {
         Object queryResult = QUERY_ENGINE.executeQuery( precondition, propSet );
         try
         {
            result = ( (Boolean) queryResult ).booleanValue(  );
            LOG.debug( "Notification precondition '" + precondition + "' evaluated to " + result );
         }
         catch ( RuntimeException re )
         {
            result = false;
            LOG.error( "Notification precondition '" + precondition
                       + "' did not evaluate to a Boolean at notification time." );
         }
      }

      return result;
   }

   private boolean evaluateSelector( QueryExpression selector,
                                     XmlObject       msg )
   throws UnknownQueryExpressionDialectException,
          QueryEvaluationErrorException,
          InvalidQueryExpressionException
   {
      boolean result;
      if ( selector == null )
      {
         result = true;
      }
      else
      {
         Object queryResult = QUERY_ENGINE.executeQuery( selector, msg );
         try
         {
            result = ( (Boolean) queryResult ).booleanValue(  );
         }
         catch ( RuntimeException re )
         {
            result = false;
            LOG.error( "Notification selector '" + selector
                       + "' did not evaluate to a Boolean at notification time." );
         }

         LOG.debug( "Notification selector '" + selector + "' evaluated to " + result );
      }

      return result;
   }

   private Topic[] evaluateTopicExpression( TopicExpression topicExpr )
   throws TopicPathDialectUnknownException
   {
      try
      {
         return m_producerResource.getTopicSet(  ).evaluateTopicExpression( topicExpr );
      }
      catch ( TopicPathDialectUnknownException tpdue )
      {
         throw tpdue;
      }
      catch ( TopicExpressionException tee )
      {
         throw new FaultException( Soap1_1Constants.FAULT_CLIENT,
                                   tee.getLocalizedMessage(  ) );
      }
   }

   private void notify( Subscription    subscription,
                        TopicExpression topicExpr,
                        XmlObject       msgXBean )
   throws Exception
   {
      synchronized ( subscription )
      {
         if ( !subscription.isPaused(  ) )
         {
            LOG.debug( "Notification being sent for subscription with id " + subscription.getID(  )
                       + "; message value: " + msgXBean );
            if ( evaluateSelector( subscription.getSelector(  ),
                                   msgXBean )
                 && evaluatePrecondition( subscription.getPrecondition(  ),
                                          subscription.getProducerResource(  ).getResourcePropertySet(  ) ) )
            {
               if ( subscription.getUseNotify(  ) )
               {
                  msgXBean = wrapMessageWithNotify( msgXBean, topicExpr );
               }

               Document          msgDom      = toDomDocument( msgXBean );
               EndpointReference consumerEPR = subscription.getConsumerReference(  );
               SOAPMessage       soapMsg     = buildSOAPMessage( msgDom, consumerEPR );
               EMITTER_POOL.execute( EmitterTask.createEmitterTask( soapMsg,
                                                                    new URL( consumerEPR.getAddress(  ).toString(  ) ) ) );
            }
         }
      }
   }

   private Document toDomDocument( XmlObject notifyDoc )
   throws ParserConfigurationException,
          SAXException,
          IOException
   {
      Document dom;
      if ( XmlBeanUtils.isDocument( notifyDoc ) )
      {
         dom = (Document) notifyDoc.newDomNode(  );
      }
      else
      {
         String notifyDocAsString = notifyDoc.xmlText( new XmlOptions(  ).setSaveOuter(  ) );
         dom = JaxpUtils.toDocument( notifyDocAsString );
      }

      return dom;
   }

   private Topic toTopic( TopicExpression topicExpr )
   throws InvalidTopicExpressionException,
          TopicNotSupportedException
   {
      Topic[] topics = new org.apache.ws.notification.topics.Topic[0];
      try
      {
         topics = m_producerResource.getTopicSet(  ).evaluateTopicExpression( topicExpr );
      }
      catch ( TopicExpressionException tee )
      {
         throw new InvalidTopicExpressionException( tee.getLocalizedMessage(  ) );
      }

      if ( topics.length == 0 )
      {
         throw new TopicNotSupportedException( "Given TopicExpression '" + topicExpr
                                               + "' did not match any Topics supported by this NotificationProducer - the WS-BaseN spec mandates that it match exactly one." );
      }

      if ( topics.length > 1 )
      {
         throw new InvalidTopicExpressionException( "Given TopicExpression matched more than one Topic supported by this NotificationProducer - the WS-BaseN spec mandates that it match exactly one." );
      }

      return topics[0];
   }

   private XmlObject wrapMessageWithNotify( XmlObject       msgXBean,
                                            TopicExpression topicExpr )
   {
      // TODO (ips, 09/28/05): creation of Notify xbean should be spec-version-sensitive
      NotifyDocument                notifyDoc      = NotifyDocument.Factory.newInstance(  );
      NotifyDocument.Notify         notify         = notifyDoc.addNewNotify(  );
      NotificationMessageHolderType notifMsgHolder = notify.addNewNotificationMessage(  );
      notifMsgHolder.setMessage( msgXBean );
      EndpointReference         producerEPR       = m_producerResource.getEndpointReference(  );
      XmlBeansEndpointReference xBeansProducerEPR = ( (XmlBeansEndpointReference) producerEPR );
      notifMsgHolder.setProducerReference( (EndpointReferenceType) xBeansProducerEPR.getXmlObject( org.apache.ws.addressing.v2003_03.AddressingConstants.NSURI_ADDRESSING_SCHEMA ) );
      TopicExpressionType topicExprType =
         (TopicExpressionType) TopicsTypeWriter.newInstance( TopicsConstants.NSURI_WSTOP_SCHEMA ).toXmlObject( topicExpr,
                                                                                                               NotificationProducerPortType.PROP_QNAME_TOPIC );
      notifMsgHolder.setTopic( topicExprType );
      return notifyDoc;
   }
}
TOP

Related Classes of org.apache.ws.notification.base.impl.NotificationProducerHelper

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.