All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.servicemix.eip.patterns.AsyncBridge Maven / Gradle / Ivy

Go to download

The ServiceMix EIP component is a routing container where different routing patterns can be deployed as service unit.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.servicemix.eip.patterns;

import java.util.Date;
import java.util.concurrent.TimeoutException;

import javax.jbi.messaging.ExchangeStatus;
import javax.jbi.messaging.InOnly;
import javax.jbi.messaging.InOut;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.NormalizedMessage;
import javax.jbi.messaging.RobustInOnly;

import org.apache.servicemix.common.util.MessageUtil;
import org.apache.servicemix.eip.EIPEndpoint;
import org.apache.servicemix.eip.support.ExchangeTarget;
import org.apache.servicemix.expression.Expression;
import org.apache.servicemix.expression.PropertyExpression;
import org.apache.servicemix.timers.Timer;
import org.apache.servicemix.timers.TimerListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The async bridge pattern is used to bridge an In-Out exchange with two In-Only
 * (or Robust-In-Only) exchanges. This pattern is the opposite of the {@link Pipeline}.
  
* The AsyncBridge uses a correlation identifier to be able to correlate the received * In-Out exchange, the In-Only sent as the request and the In-Only received as the response. * Defaults values are provided to configure those correlation ids. The default behavior * is to use the exchange id of the incoming In-Out exchange as the correlation id and set * it on the request exchange. The same property with the same value should be present on the * response exchange in order for the AsyncBridge to work. ServiceMix components usually take * care of propagating such properties, but failing to propagate it will result in errors. * * @author gnodet * @see Pipeline * * @org.apache.xbean.XBean element="async-bridge" */ public class AsyncBridge extends EIPEndpoint { public static final String CORRID = "org.apache.servicemix.eip.asyncbridge.corrid"; private final Logger logger = LoggerFactory.getLogger(AsyncBridge.class); private Expression requestCorrId = new Expression() { public Object evaluate(MessageExchange exchange, NormalizedMessage message) throws MessagingException { return exchange.getExchangeId(); } }; private String responseCorrIdProperty = CORRID; private Expression responseCorrId; private long timeout; private ExchangeTarget target; private boolean useRobustInOnly; /** * @return the timeout */ public long getTimeout() { return timeout; } /** * The timeout property controls the amount of time that the async bridge will wait for the response * after having sent the request. The default value is 0 which means that no timeout apply. If set * to a non zero value, a timer will be started when after the request is sent. When the timer * expires, the In-Out exchange will be sent back with an error status and a * {@link java.util.concurrent.TimeoutException} as the cause of the error. * The value represents the number of milliseconds to wait. * * @param timeout the timeout to set */ public void setTimeout(long timeout) { this.timeout = timeout; } /** * @return the target */ public ExchangeTarget getTarget() { return target; } /** * The target which will be used to send an In-Only or Robust-In-Only exchange to. * When receiving an In-Out exchange, the async bridge will create an In-Only request * and send it to the specified target. It then expects another In-Only exchange to * come back as the response, which will be set as the Out message on the In-Out exchange. * This property is mandatory and must be set to a valid target. * * @param target the target to set */ public void setTarget(ExchangeTarget target) { this.target = target; } /** * @return the requestCorrId */ public Expression getRequestCorrId() { return requestCorrId; } /** * The expression used to compute the correlation id used to correlate the response and * the request. The default behavior is to use the exchange id of the incoming In-Out * exchange as the correlation id. * * @param requestCorrId the requestCorrId to set * @see #setResponseCorrId(org.apache.servicemix.expression.Expression) * @see #setResponseCorrIdProperty(String) */ public void setRequestCorrId(Expression requestCorrId) { this.requestCorrId = requestCorrId; } /** * @return the responseCorrIdProperty */ public String getResponseCorrIdProperty() { return responseCorrIdProperty; } /** * Name of the property used by default to compute the correlation id on the response * exchange. * * @param responseCorrIdProperty the responseCorrIdProperty to set * @see #setRequestCorrId(org.apache.servicemix.expression.Expression) * @see #setResponseCorrId(org.apache.servicemix.expression.Expression) */ public void setResponseCorrIdProperty(String responseCorrIdProperty) { this.responseCorrIdProperty = responseCorrIdProperty; } /** * @return the responseCorrId */ public Expression getResponseCorrId() { return responseCorrId; } /** * The expression used to compute the correlation id from the response exchange. * The value computed by this expression must match the one from the {@link #setRequestCorrId} * expression. The default value is null, but if no specific expression is configured, * an expression will be created which will extract the response correlation id from the * {@link #setResponseCorrIdProperty(String)} property on the exchange. * * @param responseCorrId the responseCorrId to set * @see #setResponseCorrIdProperty(String) * @see #setRequestCorrId(org.apache.servicemix.expression.Expression) */ public void setResponseCorrId(Expression responseCorrId) { this.responseCorrId = responseCorrId; } /** * @return the useRobustInOnly */ public boolean isUseRobustInOnly() { return useRobustInOnly; } /** * Boolean flag to control if In-Only or Robust-In-Only exchange should be used * when sending the request. The default value is false which means * that an In-Only exchange will be used. When using a Robust-In-Only exchange and * when a fault is received, this fault will be sent back to the consumer on the In-Out * exchange and the response exchange (if any) would be discarded. * For both In-Only and Robust-In-Only, if the request exchange comes back with an Error * status, this error will be conveyed back to the consumer in the same way. * * @param useRobustInOnly the useRobustInOnly to set */ public void setUseRobustInOnly(boolean useRobustInOnly) { this.useRobustInOnly = useRobustInOnly; } /* * (non-Javadoc) * @see org.apache.servicemix.eip.EIPEndpoint#start() */ public void start() throws Exception { super.start(); if (responseCorrId == null) { responseCorrId = new PropertyExpression(responseCorrIdProperty); } } /* (non-Javadoc) * @see org.apache.servicemix.eip.EIPEndpoint#processSync(javax.jbi.messaging.MessageExchange) */ protected void processSync(MessageExchange exchange) throws Exception { throw new IllegalStateException(); } /* (non-Javadoc) * @see org.apache.servicemix.eip.EIPEndpoint#processAsync(javax.jbi.messaging.MessageExchange) */ protected void processAsync(MessageExchange exchange) throws Exception { throw new IllegalStateException(); } /* (non-Javadoc) * @see org.apache.servicemix.common.ExchangeProcessor#process(javax.jbi.messaging.MessageExchange) */ public void process(MessageExchange exchange) throws Exception { // Three exchanges are involved: the first InOut will be called t0, // the InOnly send will be called t1 and the InOnly received will be called t2 if (exchange.getRole() == MessageExchange.Role.PROVIDER) { // Step1: receive t0 as the first message if (exchange instanceof InOut && exchange.getStatus() == ExchangeStatus.ACTIVE) { MessageExchange t0 = exchange; MessageExchange t1; final String correlationId = (String) requestCorrId.evaluate(t0, t0.getMessage("in")); if (correlationId == null || correlationId.length() == 0) { throw new IllegalArgumentException("Could not retrieve correlation id for incoming exchange"); } store.store(correlationId + ".t0", t0); t1 = useRobustInOnly ? getExchangeFactory().createRobustInOnlyExchange() : getExchangeFactory().createInOnlyExchange(); target.configureTarget(t1, getContext()); MessageUtil.transferInToIn(t0, t1); t1.setProperty(responseCorrIdProperty, correlationId); t1.getMessage("in").setProperty(responseCorrIdProperty, correlationId); send(t1); // Receive the done / error from t0 } else if (exchange instanceof InOut && exchange.getStatus() != ExchangeStatus.ACTIVE) { MessageExchange t0 = exchange; MessageExchange t1; MessageExchange t2; final String correlationId = (String) requestCorrId.evaluate(t0, t0.getMessage("in")); t1 = (MessageExchange) store.load(correlationId + ".t1"); t2 = (MessageExchange) store.load(correlationId + ".t2"); if (t1 != null) { done(t1); } if (t2 != null) { done(t2); } // Receive the response from t2 } else if ((exchange instanceof InOnly || exchange instanceof RobustInOnly) && exchange.getStatus() == ExchangeStatus.ACTIVE) { MessageExchange t0; MessageExchange t2 = exchange; final String correlationId = (String) responseCorrId.evaluate(t2, t2.getMessage("in")); if (correlationId == null || correlationId.length() == 0) { throw new IllegalArgumentException("Could not retrieve correlation id for incoming exchange"); } t0 = (MessageExchange) store.load(correlationId + ".t0"); // The request is found and has not timed out if (t0 != null) { store.store(correlationId + ".t2", t2); MessageUtil.transferInToOut(t2, t0); send(t0); } else { done(t2); } } else { throw new IllegalStateException(); } // Handle an exchange as a CONSUMER } else { // Step 2: receive t1 response // If this is an error or a fault, transfer it from t1 to t0 and send, // else, start a timeout to wait for t2 MessageExchange t1 = exchange; // an error final String correlationId = (String) t1.getProperty(responseCorrIdProperty); if (t1.getStatus() == ExchangeStatus.ERROR) { MessageExchange t0 = (MessageExchange) store.load(correlationId + ".t0"); // t1 response may come after t0, so in case this happens, we need to discard t1 if (t0 != null) { fail(t0, t1.getError()); } // a fault ? } else if (exchange.getStatus() == ExchangeStatus.ACTIVE) { MessageExchange t0 = (MessageExchange) store.load(correlationId + ".t0"); // t1 response may come after t0, so in case this happens, we need to discard t1 if (t0 != null) { store.store(correlationId + ".t1", t1); MessageUtil.transferFaultToFault(t1, t0); send(t0); } // request sent successfully, start the timeout } else { Date exchangeTimeout = getTimeout(t1); if (exchangeTimeout != null) { getTimerManager().schedule(new TimerListener() { public void timerExpired(Timer timer) { AsyncBridge.this.onTimeout(correlationId); } }, exchangeTimeout); } } } } protected void onTimeout(String correlationId) { try { MessageExchange t0 = (MessageExchange) store.load(correlationId + ".t0"); if (t0 != null) { fail(t0, new TimeoutException()); } } catch (Exception e) { logger.debug("Exception caught when handling timeout", e); } } protected Date getTimeout(MessageExchange exchange) { if (timeout > 0) { return new Date(System.currentTimeMillis() + timeout); } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy