Package org.jboss.soa.esb.client

Source Code of org.jboss.soa.esb.client.ServiceInvoker$EPRInvoker

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2005-2006, JBoss Inc.
*/
package org.jboss.soa.esb.client;

import java.security.AccessController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.crypto.SealedObject;
import javax.security.auth.Subject;

import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.addressing.helpers.EPRHelper;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.Service;
import org.jboss.soa.esb.addressing.Call;
import org.jboss.soa.esb.addressing.EPR;
import org.jboss.soa.esb.addressing.MalformedEPRException;
import org.jboss.soa.esb.addressing.PortReference;
import org.jboss.soa.esb.addressing.util.DefaultReplyTo;
import org.jboss.soa.esb.common.Configuration;
import org.jboss.soa.esb.common.Environment;
import org.jboss.soa.esb.common.ModulePropertyManager;
import org.jboss.soa.esb.common.TransactionStrategy;
import org.jboss.soa.esb.common.TransactionStrategyException;
import org.jboss.soa.esb.couriers.Courier;
import org.jboss.soa.esb.couriers.CourierException;
import org.jboss.soa.esb.couriers.CourierFactory;
import org.jboss.soa.esb.couriers.CourierTimeoutException;
import org.jboss.soa.esb.couriers.CourierMarshalUnmarshalException;
import org.jboss.soa.esb.couriers.CourierServiceBindException;
import org.jboss.soa.esb.couriers.CourierTransportException;
import org.jboss.soa.esb.couriers.CourierUtil;
import org.jboss.soa.esb.couriers.FaultMessageException;
import org.jboss.soa.esb.couriers.TwoWayCourier;
import org.jboss.soa.esb.listeners.RegistryUtil;
import org.jboss.soa.esb.listeners.ha.LoadBalancePolicy;
import org.jboss.soa.esb.listeners.ha.ServiceClusterInfo;
import org.jboss.soa.esb.listeners.ha.ServiceClusterInfoImpl;
import org.jboss.soa.esb.listeners.message.IncompatibleTransactionScopeException;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.listeners.message.MissingServiceException;
import org.jboss.soa.esb.listeners.message.ResponseTimeoutException;
import org.jboss.soa.esb.listeners.message.errors.Factory;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.util.Type;
import org.jboss.soa.esb.services.persistence.MessageStore;
import org.jboss.soa.esb.services.persistence.RedeliverStore;
import org.jboss.soa.esb.services.registry.RegistryException;
import org.jboss.soa.esb.services.registry.ServiceNotFoundException;
import org.jboss.soa.esb.services.security.PublicCryptoUtil;
import org.jboss.soa.esb.services.security.SecurityContext;
import org.jboss.soa.esb.services.security.SecurityService;
import org.jboss.soa.esb.services.security.SecurityServiceException;
import org.jboss.soa.esb.services.security.auth.AuthenticationRequestImpl;
import org.jboss.soa.esb.util.ClassUtil;

/**
* Invoker class for managing {@link Message} delivery to a specified Service.
* <p/>
* Manages loading of {@link EPR EPRs}, {@link Courier} selection and
* message delivery. Provides a unified/simplified interface for message
* delivery.
*
* The ServiceInvoker will cache entries from the registry for the relevant
* service. This cache will be refreshed under the following conditions:
*
* (i) all of the entries fail on a delivery attempt.
* (ii) the cache times out (see property org.jboss.soa.esb.registry.cache.life): default is
* 60 seconds.
*
* @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a>
*/
public class ServiceInvoker {

    public static final String INTERNAL_SERVICE_CATEGORY = "JBossESB-Internal";
    public static final String DEAD_LETTER_SERVICE_NAME = "DeadLetterService";
    public static final String DELIVER_TO = "org.jboss.soa.esb.deliver.to";

    /*
     * Remove (suspected) dead EPRs.
     */

    private static boolean removeDeadEprs;

    /*
     * Throw an exception on delivery failure rather than retry?
     */

    private static boolean exceptionOnDeliveryFailure;

    /**
     * Class logger.
     */
    private static Logger logger = Logger.getLogger(ServiceInvoker.class);
    /**
     * The target service.
     */
    private final Service service;
    /**
     * The list of any extensions to be added to the target address.
     */
    private final List<PortReference.Extension> extensions ;
    /**
     * Load balancer.
     */
    private final LoadBalancePolicy loadBalancer;
    /**
     * Cluster info.
     */
    private ServiceClusterInfo serviceClusterInfo;
    /**
     *
     */
    private Date expirationDate;
    /**
     *
     */
    private final long registryCacheLife;
    /**
     * Dead letter channel Service invoker.
     */
    private static ServiceInvoker dlQueueInvoker;
    /**
     * Dead letter Service
     */
    public static final Service dlqService = new Service(INTERNAL_SERVICE_CATEGORY, DEAD_LETTER_SERVICE_NAME);
    /**
     * Public constructor.
     *
     * @param service The Service to which this instance will deliver messages.
     * @throws MessageDeliverException Failed to deliver message to endpoint.
     */
    public ServiceInvoker(Service service) throws MessageDeliverException {
        this(service, null) ;
    }

    /**
     * Public constructor.
     *
     * @param service The Service to which this instance will deliver messages.
     * @param extensions The extensions to be added to the target service.
     * @throws MessageDeliverException Failed to deliver message to endpoint.
     */
    public ServiceInvoker(final Service service, final List<PortReference.Extension> extensions) throws MessageDeliverException {
        AssertArgument.isNotNull(service, "service");
        this.service = service;
        this.extensions = extensions ;
        String lbClass = Configuration.getLoadBalancerPolicy();
        registryCacheLife = Long.valueOf(Configuration.getRegistryCacheLife());
        try {
            Class c = ClassUtil.forName(lbClass, this.getClass());
            loadBalancer = (LoadBalancePolicy) c.newInstance();
            loadServiceClusterInfo();
        } catch (ClassNotFoundException clf) {
            logger.error("No such LoadBalancePolicy class = " + lbClass);
            throw new MessageDeliverException(clf.getMessage(), clf);
        } catch (InstantiationException ie) {
            logger.error("Could not instatiate LoadBalancePolicy class = " + lbClass);
            throw new MessageDeliverException(ie.getMessage(), ie.getCause());
        } catch (IllegalAccessException iae) {
            logger.error("Illegal access while instantiating LoadBalancePolicy class = " + lbClass);
            throw new MessageDeliverException(iae.getMessage(), iae);
        }
    }

    /**
     * Public constructor.
     *
     * @param serviceCategory The <b>category name</b> of the Service to which this instance will
     *                        deliver messages.
     * @param serviceName     The <b>name</b> of the Service to which this instance will
     *                        deliver messages.
     * @throws MessageDeliverException Failed to deliver message to endpoint.
     */
    public ServiceInvoker(String serviceCategory, String serviceName) throws MessageDeliverException {
        this(new Service(serviceCategory, serviceName));
    }

    /**
     * Synchronously deliver the supplied message to the target service associated with this invoker instance.
     * If the To field of the Message is set, then it will be ignored in favour of the equivalent information
     * obtained through the parameters provided during instance construction.
     *
     * Redelivery on failure can be defined by setting the global org.jboss.soa.esb.dls.redeliver property in
     * the configuration file or by setting it on a per message basis as a message property. The message property
     * will be used in preference to the global value.
     *
     * @param message       The message to be delivered.
     * @param timeoutMillis Number of milliseconds before synchronous reply pickup should timeout.
     * @return Returns the reply message if the message was delivered
     *         without error, otherwise an exception is thrown.
     * @throws RegistryException       Failed to lookup Service endpoint.
     * @throws MessageDeliverException Failed to deliver message to endpoint.
     */
    public Message deliverSync(Message message, long timeoutMillis) throws MessageDeliverException, RegistryException, FaultMessageException {
        AssertArgument.isNotNull(message, "message");
        try {
            message = post(message, new EPRInvoker(timeoutMillis));
        } catch (MessageDeliverException mde) {
            if (asyncRedelivery(message) && !service.equals(dlqService)) {
                //Send a copy to the DLQ, no retries for syncDeliveries
                message.getProperties().setProperty(DELIVER_TO, service);

                logger.info("Delivering message ["+message.getHeader()+"] to DLQ.");

                deliverToDeadLetterService(message);
            }
            throw mde;
        }
        return message;
    }

    /**
     * Asynchronously deliver the supplied message to the target service associated with this invoker instance.
     * If the To field of the Message is set, then it will be ignored in favour of the equivalent information
     * obtained through the parameters provided during instance construction.
     *
     * Redelivery on failure can be defined by setting the global org.jboss.soa.esb.dls.redeliver property in
     * the configuration file or by setting it on a per message basis as a message property. The message property
     * will be used in preference to the global value.
     *
     * @param message The message to be delivered.
     * @throws MessageDeliverException Failed to deliver message, after trying all available EPRs.
     */
   
    // TODO why does this signature not throw RegistryException?!
   
    public void deliverAsync(Message message) throws MessageDeliverException
    {
        AssertArgument.isNotNull(message, "message");
        // Not interested in a reply
        try {
            post(message, new EPRInvoker());
        } catch (MessageDeliverException mde) {
            if (message.getProperties().getProperty(RedeliverStore.IS_REDELIVERY)==null
                    && asyncRedelivery(message)
                    && !service.equals(dlqService)) {
                message.getProperties().setProperty(MessageStore.CLASSIFICATION, MessageStore.CLASSIFICATION_RDLVR);
                message.getProperties().setProperty(DELIVER_TO, service);
                try {
                    logger.info("Delivering message ["+message.getHeader()+"] to RDLVRQ.");

                    deliverToDeadLetterService(message);
                } finally {
                    message.getProperties().remove(MessageStore.CLASSIFICATION);
                    message.getProperties().remove(DELIVER_TO);
                }
            } else {
                throw mde;
            }
        } catch (FaultMessageException ex) {
            throw new MessageDeliverException("Unexpected FaultMessageException during message delivery.", ex);
        }
    }

    /**
     * Deliver a message to the Dead Letter Channel Service.
     * <p/>
     * For internal package use only.
     *
     * @param message The message to be delivered to the dead letter chennel.
     * @throws RegistryException       Service endpoint lookup failure.
     * @throws MessageDeliverException Message delivery failure.
     */
    protected static synchronized void deliverToDeadLetterService(Message message) throws MessageDeliverException {
        // NB: Do not make this static method public!!!

        if (!"true".equalsIgnoreCase(Configuration.getRedeliveryDlsOn())) {
            logger.debug("org.jboss.soa.esb.dls.redeliver is turned off");
        } else {
            if (dlQueueInvoker == null) {
                dlQueueInvoker = new ServiceInvoker(dlqService);
            }

            dlQueueInvoker.deliverAsync(message);
        }
    }

    /**
     * Deliver the supplied message to the target service associated with this invoker instance.
     *
     * @param message  The message to be delivered.
     * @param eprInvoker The EPRInvoker to be used (sync or async).
     * @return Returns the message (or a reply message if synchronous) if the message was delivered
     *         without error, otherwise an exception is thrown.
     * @throws MessageDeliverException Failed to deliver message, after trying all available EPRs.
     */
    private Message post(Message message, EPRInvoker eprInvoker) throws MessageDeliverException, FaultMessageException {
        boolean staleEPRCache = true;
        boolean timeout = false ;

        /*
         * Re-attach encrypted SecurityContext to outgoing message.
         */
        final SealedObject sealedObject = SecurityContext.getSecurityContext();
        if (sealedObject != null)
        {
            message.getContext().setContext(SecurityService.CONTEXT, sealedObject);
        }
        /*
         * Re-attach encrypted AuthenticationRequest to outgoing message.
         */
        byte[] encryptedAuthRequest = getEncryptedAuthRequest();//
        if (encryptedAuthRequest != null)
        {
            message.getContext().setContext(SecurityService.AUTH_REQUEST, encryptedAuthRequest);
        }

        try {
            //We are removing dead EPRs from the serviceClusterInfo. *Previous* deliveries maybe have
            //removed EPRs that have now come back to life. We should try once more to pull a fresh list of EPRS
            //from the registry before we give up (and fail-over to redelivery at a later time in the care
            //of asyncDeliver). I think the end-user expects the message to be delivered successfully if there
            //is a healthy EPR out there. So this is really for EPR cache management (serviceClusterInfo will
            //be empty when going for the 2nd attempt, or else there delivery was successful and there will be
            //no second attempt!).
            while (staleEPRCache) {
                if ((serviceClusterInfo.getEPRs().size() == 0) || (new Date().after(expirationDate))) {
                    loadServiceClusterInfo();
   
                    if (serviceClusterInfo.getEPRs().size() == 0)
                    {
                        /*
                         * We have just reloaded the cache so a size of 0 means that the service was not located.
                         * We differentiate here by throwing a different exception.
                         */
                        throw new MissingServiceException("Registry details for service [" + service + "] could not be determined from the registry.") ;
                    }
                    staleEPRCache = false;
                }
                EPR epr;
                // Iterate over all the EPRs in the list until delivered
                while ((epr = loadBalancer.chooseEPR(serviceClusterInfo)) != null) {
                  try
                  {
                        Message replyMessage = null;
                      try {
                          replyMessage = eprInvoker.attemptDelivery(message, epr);
                      } catch (final ResponseTimeoutException rte) {
                          timeout = true ;
                      }
                      if (replyMessage != null) {
                            if(eprInvoker.synchronous) {
                                // remove the security context so that it is not exposed to the action pipeline.
                                replyMessage.getContext().removeContext(SecurityService.CONTEXT);
                                replyMessage.getContext().removeContext(SecurityService.AUTH_REQUEST);
   
                                if (Type.isFaultMessage(replyMessage)) {
                                    Factory.createExceptionFromFault(replyMessage) ;
                                }
   
                                // We've delivered it, we're done!
                                return replyMessage;
                            } else {
                                // It was an async delivery.  The replyMessage was just an indicator that
                                // the delivery succeeded.  Return null...
                                return null;
                            }
                        } else {
                          logger.info("Unresponsive EPR: " + epr+" for message: "+message.getHeader());
   
                          serviceClusterInfo.removeDeadEPR(epr);
   
                          /*
                           * So far we've only removed the EPR from the cache. Should we
                           * also remove it from the registry?
                           */
   
                          if (removeDeadEprs)
                            RegistryUtil.unregister(service.getCategory(), service.getName(), epr);
   
                          /*
                           * If the message property is set to fail immediately, or the global property is set,
                           * then don't do retries even if there are other EPRs in the list.
                           */
   
                          if (("true".equals(message.getProperties().getProperty(Environment.EXCEPTION_ON_DELIVERY_FAILURE, "false")) || exceptionOnDeliveryFailure)) {
                              if (timeout) {
                                  throw new ResponseTimeoutException("No response received for service [" + service + "], Told not to retry.") ;
                              } else {
                                  throw new MessageDeliverException("Failed to deliver message ["+message.getHeader()+"] to Service [" + service + "].  Told not to retry.");
                              }
                            }
                      }
                  }
                  catch (MalformedEPRException ex// so we can differentiate failure modes, since returning null is limiting
                  {
                    logger.debug("Invalid EPR for service (probably ESB-unaware): ignoring for message: "+message.getHeader());
   
                    serviceClusterInfo.removeDeadEPR(epr);
   
                    /*
                     * DO NOT remove from the registry - it is not dead!!
                     */
                  }
   
                }
            }
        } finally {
            message.getContext().removeContext(SecurityService.CONTEXT);
            message.getContext().removeContext(SecurityService.AUTH_REQUEST);
        }

        // Throw exception if delivery failed...
        if (timeout) {
            throw new ResponseTimeoutException("No response received for service [" + service + "].") ;
        } else {
            throw new MessageDeliverException("Failed to deliver message ["+message.getHeader()+"] to Service [" + service + "].  Check for errors.");
        }
    }

    private byte[] getEncryptedAuthRequest()
    {
    byte[] encryptedAuthRequest = AuthenticationRequestImpl.getEncryptedAuthRequest();
    if (encryptedAuthRequest != null)
        {
            final Subject subject = Subject.getSubject(AccessController.getContext());
            if (subject != null)
            {
              try
              {
                    // Decrypt the authentication request.
                    final AuthenticationRequestImpl authRequest = (AuthenticationRequestImpl) PublicCryptoUtil.INSTANCE.decrypt(encryptedAuthRequest);
                    // Get all public credentials from the authentication request.
                    final Set credentials = authRequest.getCredentials();
                    // Add all of the Subjects public credentials to the authentication request
                    credentials.addAll(subject.getPublicCredentials());
                    // "Re-encrypt" the updated authRequest.
                    byte[] encrypt = PublicCryptoUtil.INSTANCE.encrypt(authRequest);
                    encryptedAuthRequest = encrypt;
                    AuthenticationRequestImpl.setEncryptedAuthRequest(encrypt);
              }
              catch (SecurityServiceException e)
              {
                  logger.error("SecurityException", e);
              }
            }
        }
        return encryptedAuthRequest;
    }

    /**
     * Get the details of Service to which this invoker instance is delivering messages.
     *
     * @return The Service details.
     */
    public Service getService() {
        return service;
    }

    /**
     * Get the Service category name for the Service for which this instance is delivering messages.
     *
     * @return Service Category.
     */
    public String getServiceCategory() {
        return service.getCategory();
    }

    /**
     * Get the Service name for the Service for which this instance is delivering messages.
     *
     * @return Service name.
     */
    public String getServiceName() {
        return service.getName();
    }

    /**
     * Get the reply to address for synchronous delivery.
     *
     * @param toEpr The to address.
     * @return The replyTo address.
     * @throws ConfigurationException Unable to support synchronous reply on 'to' address.
     */
    protected EPR getReplyToAddress(EPR toEpr) throws ConfigurationException {
        // This method just allows us to override Courier lookup during unit testing.
        try {
            return DefaultReplyTo.getReplyTo(toEpr);
        } catch (CourierException e) {
            throw new ConfigurationException("Bad configuration. Unable to support synchronous reply on 'to' address " + toEpr, e);
        } catch (MalformedEPRException e) {
            throw new ConfigurationException("Bad configuration. Unable to support synchronous reply on 'to' address " + toEpr, e);
        }
    }

    /**
     * Get a {@link org.jboss.soa.esb.couriers.Courier} for the supplied EPR.
     *
     * @param epr The EPR for which a {@link org.jboss.soa.esb.couriers.Courier}
     *            is being sought.
     * @return The courier for the EPR.
     * @throws CourierException      A courier implementation cannot be created.
     * @throws MalformedEPRException Bad EPR.
     */
    protected TwoWayCourier getCourier(EPR epr) throws CourierException, MalformedEPRException {
        // This method just allows us to override Courier lookup during unit testing.
        return CourierFactory.getInstance().getMessageCourier(epr);
    }

    /**
     * Loads the EPRs fresh from the Registry. Right now we will do this every minute
     * until we can expect to get updates from the registry. For now this should work
     * just fine.
     *
     * @throws MessageDeliverException Registry lookup failure.
     */
    public void loadServiceClusterInfo() throws MessageDeliverException {
        List<EPR> serviceEprs = new ArrayList<EPR>();
        try {
            serviceEprs = RegistryUtil.getEprs(service.getCategory(), service.getName());
            if (logger.isDebugEnabled()) {
                for (EPR epr : serviceEprs) {
                    try {
                        logger.debug("EPR=" + epr + " XML=" + EPRHelper.toXMLString(epr));
                    } catch (Exception me) {
                        logger.error(me.getMessage(), me);
                    }
                }
            }
        } catch (ServiceNotFoundException snfe) {
            logger.info("Service: " + service + " not found in the registry");
        } catch (RegistryException e) {
            throw new MessageDeliverException(e.getMessage(), e);
        }
        serviceClusterInfo = new ServiceClusterInfoImpl(service.getName(), serviceEprs);
        expirationDate = new Date(java.lang.System.currentTimeMillis() + registryCacheLife);
    }
   
    /*
     * Is redelivery overridden on a per message basis? If not, use the global setting.
     */
   
    private boolean asyncRedelivery (final Message message)
    {
        Object perMessage = message.getProperties().getProperty(Environment.REDELIVER_DLS_SERVICE_ON);
       
        if (perMessage == null) // user does not care so use the global property
            return "true".equalsIgnoreCase(Configuration.getRedeliveryDlsOn());
        else
        {
            // use whatever is set on the message in preference to global value
           
            return "true".equalsIgnoreCase((String) perMessage);
        }
    }
   
    private class EPRInvoker {

        private boolean synchronous = false;
        private long timeout;

        /**
         * Create an asynchronous EPRInvoker instance.
         */
        private EPRInvoker() {
            synchronous = false;
        }

        /**
         * Create a synchronous EPRInvoker instance with the specified timeout.
         */
        private EPRInvoker(long timeout) {
            synchronous = true;
            this.timeout = timeout;
        }


        /**
         * Attempt to deliver the supplied message using the supplied EPR.
         *
         * @param message     The message to be delivered.
         * @param epr         The EPR to be used in the delivery attempt.
         * @return Returns the message (or a reply message if synchronous) if the message was delivered
         *         without error, otherwise null.
         */
        private Message attemptDelivery(Message message, EPR epr) throws FaultMessageException, MalformedEPRException, MessageDeliverException {
            TwoWayCourier courier = null;

            final EPR targetEPR ;
            if ((extensions != null) && (extensions.size() > 0))
            {
                targetEPR = EPRHelper.copyEPR(epr, extensions) ;
            }
            else
            {
                targetEPR = epr ;
            }
            // Get a courier for the EPR...
            try {
                courier = getCourier(targetEPR);
            } catch (CourierException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Courier lookup failed for EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"].", e);
                }
            } catch (MalformedEPRException e) {
              // probably an ESB-unaware EPR in the registry!!

                logger.info("Badly formed EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"]." + e.getMessage());

                throw e;
            } catch (Throwable t) {
                logger.warn("Unexpected exception during Courier lookup for EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"].", t);
            }

            // Try delivering the message using the courier we just looked up....

            if (courier != null) {
                // make sure the message header does not change when we exit
                final Call call = message.getHeader().getCall() ;
                final EPR currentToEpr = call.getTo() ;
                final EPR currentReplyToEpr = call.getReplyTo() ;

                try {
                    call.setTo(targetEPR);

                    final EPR replyToEPR ;
                    if (synchronous) {
                        /*
                         * Currently all couriers that have transactional semantics work in a manner similar
                         * to JMS, i.e., messages are not delivered on to a queue (or to an endpoint) until
                         * the enclosing transaction has committed. In a synchronous invocation this will
                         * result in timeouts if the sending thread is also the terminating thread. We check
                         * for this here and throw an exception before we try to do a send so the application can
                         * figure it out.
                         *
                         * Note: if the transactional semantics change (e.g., to support true distributed transactions)
                         * then this will need to be modified too.
                         */
                       
                        if (isTransactional())
                            throw new IncompatibleTransactionScopeException("Request-response attempt within running transaction controlling request delivery! Using epr [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"]");
                       
                        replyToEPR = (currentReplyToEpr == null ? getReplyToAddress(targetEPR) : currentReplyToEpr) ;

                        if (replyToEPR == null) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Not using epr [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"]. No reply-to address available for synchronous response.");
                            }
                            return null;
                        }
                        call.setReplyTo(replyToEPR);
                    } else {
                        replyToEPR = null ;
                    }
                    if (courier.deliver(message)) {
                        if (synchronous) {
                            courier.cleanup() ;
                            // JBESB-1016 replyToEPR has to be non-null or we'd have dropped out by this point!

                            // do we need to do this for synchronous calls? Vagueries of Couriers?

                            courier.setReplyToEpr(replyToEPR);
                            final Message response = courier.pickup(timeout);
                            if (response == null) {
                                throw new ResponseTimeoutException("No response received within timeout period") ;
                            }
                            return response ;
                        } else {
                            return message;
                        }
                    }
                } catch (FaultMessageException e) {
                    throw e;
                } catch (final CourierServiceBindException e) {
                    // meant to be masked by the SI fail-over

                    if (logger.isDebugEnabled()) {
                        logger.debug("Caught service lookup exception for EPR [" + targetEPR + "] and Service [" + service + "] and Message ["+message.getHeader()+"]. ", e);
                    }

                    // could be stale EPR, so move on to next entry in registry.
                } catch (final CourierMarshalUnmarshalException e) {
                    logger.warn("Courier indicated (un)marshal related error "+e+" during delivery to EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"]. " + e.getMessage());

                    throw new MessageDeliverException("Caught (un)marshal related exception during attempted send/receive.", e);
                } catch (final CourierTransportException e) {
                    // meant to be masked by the SI fail-over

                    if (logger.isDebugEnabled()) {
                        logger.debug("Courier indicated transport related error "+e+" during send/receive with EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"]. ", e);
                    }
                } catch (CourierException e) {
                    // probable config error. Log it and move on to next EPR/service entry.

                    logger.warn("Possible configuration error while using Courier for EPR [" + targetEPR + "] and Service [" + service + "] and Message ["+message.getHeader()+"]. " + e.getMessage());
                } catch (MalformedEPRException e) {
                    // Hmmmm???... Can this really happen?  The Courier has already been created.  Haven't we already validated the EPR during the Courier lookup (above)??
                    logger.error("Unexpected error.  Badly formed EPR [" + targetEPR + "] for Service [" + service + "]. But the EPR has already been validated!!");

                    throw e;
                } catch (final CourierTimeoutException ex) {
                    logger.error("Response timeout using Courier for EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"].");

                    // timeout from synchronous invocation
                   
                    // would like to make this an independent exception (not inherit from MDE). But signatures and applications would break.
                   
                    throw new ResponseTimeoutException("Caught response timeout!", ex);
                } catch (final MessageDeliverException mde) {
                    throw mde ;
                } catch (Throwable t) {
                    logger.error("Unexpected throwable during attempted message delivery using Courier for EPR [" + targetEPR + "] for Service [" + service + "] and Message ["+message.getHeader()+"].", t);

                    // we don't know what state we're in so better to bail-out now!

                    throw new MessageDeliverException("Caught unexpected throwable during send. Bailing-out!", t);
                } finally {
                    CourierUtil.cleanCourier(courier);

                    // put back the old To since we will have changed it.

                    call.setTo(currentToEpr) ;
                    call.setReplyTo(currentReplyToEpr) ;
                }
            }

            return null;
        }
    }

    private boolean isTransactional() throws MessageDeliverException
    {
        boolean transactional;
       
        try
        {
            TransactionStrategy txStrategy = TransactionStrategy.getTransactionStrategy(true);
            Object txHandle = ((txStrategy == null) ? null : txStrategy.getTransaction());
            boolean isActive = ((txStrategy == null) ? false : txStrategy.isActive());

            transactional = (txHandle != null);

            /*
            * Make sure the current transaction is still active! If we
            * have previously slept, then the timeout may be longer than that
            * associated with the transaction.
            */

            if (transactional && !isActive)
            {
                throw new MessageDeliverException("Associated transaction is no longer active!");
            }
        }
        catch (final TransactionStrategyException ex)
        {
            throw new MessageDeliverException("Could not determine transactionality.", ex);
        }
       
        return transactional;
    }
   
    static
    {
      String pruneDead = ModulePropertyManager.getPropertyManager(ModulePropertyManager.CORE_MODULE).getProperty(Environment.REMOVE_DEAD_EPR, "false");

      if ("true".equalsIgnoreCase(pruneDead))
      {
        removeDeadEprs = true;
      }
      else
      {
        removeDeadEprs = false;
      }

      String exceptionOnFailure = ModulePropertyManager.getPropertyManager(ModulePropertyManager.CORE_MODULE).getProperty(Environment.EXCEPTION_ON_DELIVERY_FAILURE, "false");

      if ("true".equalsIgnoreCase(exceptionOnFailure))
      {
        exceptionOnDeliveryFailure = true;
      }
      else
      {
        exceptionOnDeliveryFailure = false;
      }
    }
}
TOP

Related Classes of org.jboss.soa.esb.client.ServiceInvoker$EPRInvoker

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.