/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.synapse.message.senders.blocking;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.OperationClient;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.context.ServiceGroupContext;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.AxisServiceGroup;
import org.apache.axis2.description.WSDL2Constants;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.SynapseException;
import org.apache.synapse.SynapseHandler;
import org.apache.synapse.commons.json.JsonUtil;
import org.apache.synapse.core.axis2.AnonymousServiceFactory;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.endpoints.AbstractEndpoint;
import org.apache.synapse.endpoints.Endpoint;
import org.apache.synapse.endpoints.EndpointDefinition;
import org.apache.synapse.endpoints.IndirectEndpoint;
import org.apache.synapse.endpoints.TemplateEndpoint;
import org.apache.synapse.util.MessageHelper;
import javax.xml.namespace.QName;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BlockingMsgSender {
public final static String DEFAULT_CLIENT_REPO = "./repository/deployment/client";
public final static String DEFAULT_AXIS2_XML = "./repository/conf/axis2/axis2_blocking_client.xml";
private static Log log = LogFactory.getLog(BlockingMsgSender.class);
private String clientRepository = null;
private String axis2xml = null;
private ConfigurationContext configurationContext = null;
boolean initClientOptions = true;
private final static String LOCAL_ANON_SERVICE = "__LOCAL_ANON_SERVICE__";
private Pattern errorMsgPattern = Pattern.compile("Transport error: \\d{3} .*");
private Pattern statusCodePattern = Pattern.compile("\\d{3}");
public void init() {
try {
if (configurationContext == null) {
configurationContext
= ConfigurationContextFactory.createConfigurationContextFromFileSystem(
clientRepository != null ? clientRepository : DEFAULT_CLIENT_REPO,
axis2xml != null ? axis2xml : DEFAULT_AXIS2_XML);
}
} catch (AxisFault e) {
handleException("Error initializing BlockingMessageSender", e);
}
}
public MessageContext send(Endpoint endpoint, MessageContext synapseInMsgCtx)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("Start Sending the Message ");
}
if (endpoint instanceof IndirectEndpoint) {
String endpointKey = ((IndirectEndpoint) endpoint).getKey();
endpoint = synapseInMsgCtx.getEndpoint(endpointKey);
}
if (endpoint instanceof TemplateEndpoint) {
endpoint = ((TemplateEndpoint) endpoint).getRealEndpoint();
}
AbstractEndpoint abstractEndpoint = (AbstractEndpoint) endpoint;
if (!abstractEndpoint.isLeafEndpoint()) {
handleException("Endpoint Type not supported");
}
// clear the message context properties related to endpoint in last service invocation
Set keySet = synapseInMsgCtx.getPropertyKeySet();
if (keySet != null) {
keySet.remove(EndpointDefinition.DYNAMIC_URL_VALUE);
}
abstractEndpoint.executeEpTypeSpecificFunctions(synapseInMsgCtx);
EndpointDefinition endpointDefinition = abstractEndpoint.getDefinition();
org.apache.axis2.context.MessageContext axisInMsgCtx =
((Axis2MessageContext) synapseInMsgCtx).getAxis2MessageContext();
org.apache.axis2.context.MessageContext axisOutMsgCtx =
new org.apache.axis2.context.MessageContext();
String endpointReferenceValue = null;
if (endpointDefinition.getAddress() != null) {
endpointReferenceValue = endpointDefinition.getAddress();
} else if (axisInMsgCtx.getTo() != null) {
endpointReferenceValue = axisInMsgCtx.getTo().getAddress();
} else {
handleException("Service url, Endpoint or 'To' header is required");
}
EndpointReference epr = new EndpointReference(endpointReferenceValue);
axisOutMsgCtx.setTo(epr);
AxisService anonymousService;
if (endpointReferenceValue != null &&
endpointReferenceValue.startsWith(Constants.TRANSPORT_LOCAL)) {
configurationContext = axisInMsgCtx.getConfigurationContext();
anonymousService =
AnonymousServiceFactory.getAnonymousService(
configurationContext.getAxisConfiguration(),
LOCAL_ANON_SERVICE);
} else {
anonymousService =
AnonymousServiceFactory.getAnonymousService(
null,
configurationContext.getAxisConfiguration(),
endpointDefinition.isAddressingOn() | endpointDefinition.isReliableMessagingOn(),
endpointDefinition.isReliableMessagingOn(),
endpointDefinition.isSecurityOn(),
false);
}
axisOutMsgCtx.setConfigurationContext(configurationContext);
axisOutMsgCtx.setEnvelope(axisInMsgCtx.getEnvelope());
axisOutMsgCtx.setProperty(HTTPConstants.NON_ERROR_HTTP_STATUS_CODES,
axisInMsgCtx.getProperty(HTTPConstants.NON_ERROR_HTTP_STATUS_CODES));
axisOutMsgCtx.setProperty(HTTPConstants.ERROR_HTTP_STATUS_CODES,
axisInMsgCtx.getProperty(HTTPConstants.ERROR_HTTP_STATUS_CODES));
axisOutMsgCtx.setProperty(SynapseConstants.DISABLE_CHUNKING,
axisInMsgCtx.getProperty(SynapseConstants.DISABLE_CHUNKING));
// Fill MessageContext
BlockingMsgSenderUtils.fillMessageContext(endpointDefinition, axisOutMsgCtx, synapseInMsgCtx);
if (JsonUtil.hasAJsonPayload(axisInMsgCtx)) {
JsonUtil.cloneJsonPayload(axisInMsgCtx, axisOutMsgCtx);
}
Options clientOptions;
if (initClientOptions) {
clientOptions = new Options();
} else {
clientOptions = axisInMsgCtx.getOptions();
clientOptions.setTo(epr);
}
// Fill Client options
BlockingMsgSenderUtils.fillClientOptions(endpointDefinition, clientOptions, synapseInMsgCtx);
anonymousService.getParent().addParameter(SynapseConstants.HIDDEN_SERVICE_PARAM, "true");
ServiceGroupContext serviceGroupContext =
new ServiceGroupContext(configurationContext,
(AxisServiceGroup) anonymousService.getParent());
ServiceContext serviceCtx = serviceGroupContext.getServiceContext(anonymousService);
axisOutMsgCtx.setServiceContext(serviceCtx);
// Invoke
boolean isOutOnly = isOutOnly(synapseInMsgCtx, axisOutMsgCtx);
try {
if (isOutOnly) {
sendRobust(axisOutMsgCtx, clientOptions, anonymousService, serviceCtx, synapseInMsgCtx);
final String httpStatusCode =
String.valueOf(axisOutMsgCtx.getProperty(SynapseConstants.HTTP_SENDER_STATUSCODE))
.trim();
/*
* Though this is OUT_ONLY operation, we need to set the
* response Status code so that others can make use of it.
*/
axisInMsgCtx.setProperty(SynapseConstants.HTTP_SC, httpStatusCode);
} else {
org.apache.axis2.context.MessageContext result =
sendReceive(axisOutMsgCtx, clientOptions, anonymousService, serviceCtx, synapseInMsgCtx);
synapseInMsgCtx.setEnvelope(result.getEnvelope());
if (JsonUtil.hasAJsonPayload(result)) {
JsonUtil.cloneJsonPayload(result, ((Axis2MessageContext) synapseInMsgCtx).getAxis2MessageContext());
}
final String statusCode =
String.valueOf(result.getProperty(SynapseConstants.HTTP_SENDER_STATUSCODE))
.trim();
/*
* We need to set the response status code so that users can
* fetch it later.
*/
axisInMsgCtx.setProperty(SynapseConstants.HTTP_SC, statusCode);
if ("false".equals(synapseInMsgCtx.getProperty(
SynapseConstants.BLOCKING_SENDER_PRESERVE_REQ_HEADERS))) {
axisInMsgCtx.setProperty(
org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS,
result.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS));
}
synapseInMsgCtx.setProperty(SynapseConstants.BLOCKING_SENDER_ERROR, "false");
return synapseInMsgCtx;
}
} catch (Exception ex) {
/*
* Extract the HTTP status code from the Exception message.
*/
final String errorStatusCode = extractStatusCodeFromException(ex);
axisInMsgCtx.setProperty(SynapseConstants.HTTP_SC, errorStatusCode);
if (!isOutOnly) {
//axisOutMsgCtx.getTransportOut().getSender().cleanup(axisOutMsgCtx);
synapseInMsgCtx.setProperty(SynapseConstants.BLOCKING_SENDER_ERROR, "true");
synapseInMsgCtx.setProperty(SynapseConstants.ERROR_EXCEPTION, ex);
if (ex instanceof AxisFault) {
AxisFault fault = (AxisFault) ex;
int errorCode = SynapseConstants.BLOCKING_SENDER_OPERATION_FAILED;
if (fault.getFaultCode() != null && fault.getFaultCode().getLocalPart() != null &&
!"".equals(fault.getFaultCode().getLocalPart())) {
try {
errorCode = Integer.parseInt(fault.getFaultCode().getLocalPart());
} catch (NumberFormatException e) {
errorCode = SynapseConstants.BLOCKING_SENDER_OPERATION_FAILED;
}
}
synapseInMsgCtx.setProperty(SynapseConstants.ERROR_CODE, errorCode);
synapseInMsgCtx.setProperty(SynapseConstants.ERROR_MESSAGE, fault.getMessage());
synapseInMsgCtx.setProperty(SynapseConstants.ERROR_DETAIL,
fault.getDetail() != null ?
fault.getDetail().getText() : "");
org.apache.axis2.context.MessageContext faultMC = fault.getFaultMessageContext();
if (faultMC != null) {
Object statusCode = faultMC.getProperty(SynapseConstants.HTTP_SENDER_STATUSCODE);
synapseInMsgCtx.setProperty(SynapseConstants.HTTP_SC, statusCode);
axisInMsgCtx.setProperty(SynapseConstants.HTTP_SC, statusCode);
synapseInMsgCtx.setEnvelope(faultMC.getEnvelope());
}
}
return synapseInMsgCtx;
}
handleException("Error sending Message to url : " +
((AbstractEndpoint) endpoint).getDefinition().getAddress(), ex);
}
return null;
}
private void sendRobust(org.apache.axis2.context.MessageContext axisOutMsgCtx,
Options clientOptions, AxisService anonymousService,
ServiceContext serviceCtx, MessageContext synapseInMsgCtx) throws AxisFault {
this.invokeHandlers(synapseInMsgCtx, false);
AxisOperation axisAnonymousOperation =
anonymousService.getOperation(new QName(AnonymousServiceFactory.OUT_ONLY_OPERATION));
OperationClient operationClient =
axisAnonymousOperation.createClient(serviceCtx, clientOptions);
operationClient.addMessageContext(axisOutMsgCtx);
axisOutMsgCtx.setAxisMessage(
axisAnonymousOperation.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE));
operationClient.execute(true);
axisOutMsgCtx.getTransportOut().getSender().cleanup(axisOutMsgCtx);
}
private org.apache.axis2.context.MessageContext sendReceive(
org.apache.axis2.context.MessageContext axisOutMsgCtx,
Options clientOptions,
AxisService anonymousService,
ServiceContext serviceCtx, MessageContext synapseInMsgCtx)
throws AxisFault {
AxisOperation axisAnonymousOperation =
anonymousService.getOperation(new QName(AnonymousServiceFactory.OUT_IN_OPERATION));
OperationClient operationClient =
axisAnonymousOperation.createClient(serviceCtx, clientOptions);
operationClient.addMessageContext(axisOutMsgCtx);
axisOutMsgCtx.setAxisMessage(
axisAnonymousOperation.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE));
this.invokeHandlers(synapseInMsgCtx, false);
operationClient.execute(true);
org.apache.axis2.context.MessageContext resultMsgCtx =
operationClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
org.apache.axis2.context.MessageContext returnMsgCtx =
new org.apache.axis2.context.MessageContext();
if (resultMsgCtx.getEnvelope() != null) {
returnMsgCtx.setEnvelope(MessageHelper.cloneSOAPEnvelope(resultMsgCtx.getEnvelope()));
if (JsonUtil.hasAJsonPayload(resultMsgCtx)) {
JsonUtil.cloneJsonPayload(resultMsgCtx, returnMsgCtx);
}
} else {
if (axisOutMsgCtx.isSOAP11()) {
returnMsgCtx.setEnvelope(OMAbstractFactory.getSOAP11Factory().getDefaultEnvelope());
} else {
returnMsgCtx.setEnvelope(OMAbstractFactory.getSOAP12Factory().getDefaultEnvelope());
}
}
returnMsgCtx.setProperty(SynapseConstants.HTTP_SENDER_STATUSCODE,
resultMsgCtx.getProperty(SynapseConstants.HTTP_SENDER_STATUSCODE));
axisOutMsgCtx.getTransportOut().getSender().cleanup(axisOutMsgCtx);
returnMsgCtx.setProperty(
org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS,
resultMsgCtx.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS));
this.invokeHandlers(synapseInMsgCtx, true);
return returnMsgCtx;
}
private boolean isOutOnly(MessageContext messageIn,
org.apache.axis2.context.MessageContext axis2Ctx) {
return "true".equals(messageIn.getProperty(SynapseConstants.OUT_ONLY)) ||
axis2Ctx.getOperationContext() != null &&
WSDL2Constants.MEP_URI_IN_ONLY.equals(axis2Ctx.getOperationContext().
getAxisOperation().getMessageExchangePattern());
}
public void setClientRepository(String clientRepository) {
this.clientRepository = clientRepository;
}
public void setAxis2xml(String axis2xml) {
this.axis2xml = axis2xml;
}
public void setConfigurationContext(ConfigurationContext configurationContext) {
this.configurationContext = configurationContext;
}
public void setInitClientOptions(boolean initClientOptions) {
this.initClientOptions = initClientOptions;
}
private void handleException(String msg, Exception e) {
log.error(msg, e);
throw new SynapseException(msg, e);
}
private void handleException(String msg) {
log.error(msg);
throw new SynapseException(msg);
}
private String extractStatusCodeFromException(Exception exception) {
String responseStatusCode = "";
Matcher errMsgMatcher = errorMsgPattern.matcher(exception.getMessage());
Matcher statusCodeMatcher = statusCodePattern.matcher(exception.getMessage());
while (errMsgMatcher.find() && statusCodeMatcher.find()) {
responseStatusCode = statusCodeMatcher.group().trim();
break;
}
return responseStatusCode;
}
/**
* Invoke Synapse Handlers
*
* @param synCtx synapse message context
* @param isResponse whether message is response path or not
*/
private void invokeHandlers(MessageContext synCtx, boolean isResponse) {
Iterator<SynapseHandler> iterator =
synCtx.getEnvironment().getSynapseHandlers().iterator();
if (iterator.hasNext()) {
if (isResponse) {
do {
SynapseHandler handler = iterator.next();
if (!handler.handleResponseInFlow(synCtx)) {
log.warn("Synapse not executed in the response in path");
}
} while (iterator.hasNext());
} else {
do {
SynapseHandler handler = iterator.next();
if (!handler.handleRequestOutFlow(synCtx)) {
log.warn("Synapse not executed in the request out path");
}
} while (iterator.hasNext());
}
}
}
}