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