/* * Copyright (c) 2005-2014, 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.wso2.carbon.inbound.endpoint.protocol.http; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axis2.AxisFault; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.MessageContext; import org.apache.axis2.context.OperationContext; import org.apache.axis2.context.ServiceContext; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.InOutAxisOperation; import org.apache.axis2.util.Utils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.protocol.HTTP; import org.apache.synapse.SynapseException; import org.apache.synapse.SynapseConstants; import org.apache.synapse.core.axis2.Axis2MessageContext; import org.apache.synapse.core.axis2.MessageContextCreatorForAxis2; import org.apache.synapse.inbound.InboundEndpoint; import org.apache.synapse.inbound.InboundEndpointConstants; import org.apache.synapse.mediators.MediatorFaultHandler; import org.apache.synapse.mediators.base.SequenceMediator; import org.apache.synapse.rest.RESTRequestHandler; import org.apache.synapse.transport.customlogsetter.CustomLogSetter; import org.apache.synapse.transport.passthru.ServerWorker; import org.apache.synapse.transport.passthru.SourceRequest; import org.apache.synapse.transport.passthru.config.SourceConfiguration; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils; import org.wso2.carbon.inbound.endpoint.protocol.http.management.HTTPEndpointManager; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import java.io.OutputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Create SynapseMessageContext from HTTP Request and inject it to the sequence in a synchronous manner * This is the worker for HTTP inbound related requests. */ public class InboundHttpServerWorker extends ServerWorker { private static final Log log = LogFactory.getLog(InboundHttpServerWorker.class); private SourceRequest request = null; private int port; private String tenantDomain; private RESTRequestHandler restHandler; private Pattern dispatchPattern; private Matcher patternMatcher; public InboundHttpServerWorker(int port, String tenantDomain, SourceRequest sourceRequest, SourceConfiguration sourceConfiguration, OutputStream outputStream) { super(sourceRequest, sourceConfiguration, outputStream); this.request = sourceRequest; this.port = port; this.tenantDomain = tenantDomain; restHandler = new RESTRequestHandler(); } public void run() { if (request != null) { try { //get already created axis2 context from ServerWorker MessageContext axis2MsgContext = getRequestContext(); //create Synapse Message Context org.apache.synapse.MessageContext synCtx = createSynapseMessageContext(request, axis2MsgContext); updateAxis2MessageContextForSynapse(synCtx); setInboundProperties(synCtx); String method = request.getRequest() != null ? request.getRequest(). getRequestLine().getMethod().toUpperCase() : ""; processHttpRequestUri(axis2MsgContext, method); String endpointName = HTTPEndpointManager.getInstance().getEndpointName(port, tenantDomain); if (endpointName == null) { handleException("Endpoint not found for port : " + port + "" + " tenant domain : " + tenantDomain); } InboundEndpoint endpoint = synCtx.getConfiguration().getInboundEndpoint(endpointName); if (endpoint == null) { log.error("Cannot find deployed inbound endpoint " + endpointName + "for process request"); return; } // OpenEventCollector.reportEntryEvent(synCtx, endpointName, endpoint.getAspectConfiguration(), // ComponentType.INBOUNDENDPOINT); CustomLogSetter.getInstance().setLogAppender(endpoint.getArtifactContainerName()); if (!isRESTRequest(axis2MsgContext, method)) { if (request.isEntityEnclosing()) { processEntityEnclosingRequest(axis2MsgContext, false); } else { processNonEntityEnclosingRESTHandler(null, axis2MsgContext, false); } } else { AxisOperation axisOperation = ((Axis2MessageContext) synCtx).getAxis2MessageContext().getAxisOperation(); ((Axis2MessageContext) synCtx).getAxis2MessageContext().setAxisOperation(null); String contentTypeHeader = request.getHeaders().get(HTTP.CONTENT_TYPE); SOAPEnvelope soapEnvelope = handleRESTUrlPost(contentTypeHeader); processNonEntityEnclosingRESTHandler(soapEnvelope, axis2MsgContext, false); ((Axis2MessageContext) synCtx).getAxis2MessageContext().setAxisOperation(axisOperation); } dispatchPattern = HTTPEndpointManager.getInstance().getPattern(tenantDomain, port); boolean continueDispatch = true; if (dispatchPattern != null) { patternMatcher = dispatchPattern.matcher(request.getUri()); if (!patternMatcher.matches()) { if (log.isDebugEnabled()) { log.debug("Requested URI does not match given dispatch regular expression."); } continueDispatch = false; } } if (continueDispatch && dispatchPattern != null) { boolean processedByAPI = false; // Trying to dispatch to an API processedByAPI = restHandler.process(synCtx); if (log.isDebugEnabled()) { log.debug("Dispatch to API state : enabled, Message is " + (!processedByAPI ? "NOT" : "") + "processed by an API"); } if (!processedByAPI) { //check the validity of message routing to axis2 path boolean isAxis2Path = isAllowedAxis2Path(synCtx); if (isAxis2Path) { //create axis2 message context again to avoid settings updated above axis2MsgContext = createMessageContext(null, request); processHttpRequestUri(axis2MsgContext, method); //set inbound properties for axis2 context setInboundProperties(axis2MsgContext); if (!isRESTRequest(axis2MsgContext, method)) { if (request.isEntityEnclosing()) { processEntityEnclosingRequest(axis2MsgContext, isAxis2Path); } else { processNonEntityEnclosingRESTHandler(null, axis2MsgContext, isAxis2Path); } } else { String contentTypeHeader = request.getHeaders().get(HTTP.CONTENT_TYPE); SOAPEnvelope soapEnvelope = handleRESTUrlPost(contentTypeHeader); processNonEntityEnclosingRESTHandler(soapEnvelope,axis2MsgContext,true); } } else { //this case can only happen regex exists and it DOES match //BUT there is no api or proxy found message to be injected //should be routed to the main sequence instead inbound defined sequence injectToMainSequence(synCtx, endpoint); } } } else if (continueDispatch && dispatchPattern == null) { // else if for clarity compiler will optimize injectToSequence(synCtx, endpoint); } else { //this case can only happen regex exists and it DOES NOT match //should be routed to the main sequence instead inbound defined sequence injectToMainSequence(synCtx, endpoint); } // send ack for client if needed sendAck(axis2MsgContext); } catch (Exception e) { log.error("Exception occurred when running " + InboundHttpServerWorker.class.getName(), e); } } else { log.error("InboundSourceRequest cannot be null"); } } private void injectToMainSequence(org.apache.synapse.MessageContext synCtx, InboundEndpoint endpoint) { SequenceMediator injectingSequence = (SequenceMediator) synCtx.getMainSequence(); SequenceMediator faultSequence = getFaultSequence(synCtx, endpoint); MediatorFaultHandler mediatorFaultHandler = new MediatorFaultHandler(faultSequence); synCtx.pushFaultHandler(mediatorFaultHandler); /* handover synapse message context to synapse environment for inject it to given sequence in synchronous manner*/ if (log.isDebugEnabled()) { log.debug("injecting message to sequence : " + endpoint.getInjectingSeq()); } synCtx.getEnvironment().injectMessage(synCtx, injectingSequence); } private void injectToSequence(org.apache.synapse.MessageContext synCtx, InboundEndpoint endpoint) { // Get injecting sequence for synapse engine SequenceMediator injectingSequence = null; if (endpoint.getInjectingSeq() != null) { injectingSequence = (SequenceMediator) synCtx.getSequence(endpoint.getInjectingSeq()); } if (injectingSequence == null) { injectingSequence = (SequenceMediator) synCtx.getMainSequence(); } SequenceMediator faultSequence = getFaultSequence(synCtx, endpoint); MediatorFaultHandler mediatorFaultHandler = new MediatorFaultHandler(faultSequence); synCtx.pushFaultHandler(mediatorFaultHandler); /* handover synapse message context to synapse environment for inject it to given sequence in synchronous manner*/ if (log.isDebugEnabled()) { log.debug("injecting message to sequence : " + endpoint.getInjectingSeq()); } synCtx.setProperty("inbound.endpoint.name", endpoint.getName()); synCtx.getEnvironment().injectMessage(synCtx, injectingSequence); } private SequenceMediator getFaultSequence(org.apache.synapse.MessageContext synCtx, InboundEndpoint endpoint) { SequenceMediator faultSequence = null; if (endpoint.getOnErrorSeq() != null) { faultSequence = (SequenceMediator) synCtx.getSequence(endpoint.getOnErrorSeq()); } if (faultSequence == null) { faultSequence = (SequenceMediator) synCtx.getFaultSequence(); } return faultSequence; } /** * Set Inbound Related Properties for Synapse Message Context * * @param msgContext Synapse Message Context of incoming request */ private void setInboundProperties(org.apache.synapse.MessageContext msgContext) { msgContext.setProperty(SynapseConstants.IS_INBOUND, true); msgContext.setProperty(InboundEndpointConstants.INBOUND_ENDPOINT_RESPONSE_WORKER, new InboundHttpResponseSender()); msgContext.setWSAAction(request.getHeaders().get(InboundHttpConstants.SOAP_ACTION)); } /** * Set Inbound Related Properties for Axis2 Message Context * * @param axis2Context Axis2 Message Context of incoming request */ private void setInboundProperties(MessageContext axis2Context) { axis2Context.setProperty(SynapseConstants.IS_INBOUND, true); } protected void handleException(String msg) { log.error(msg); throw new SynapseException(msg); } /** * Checks whether the message should be routed to Axis2 path * * @param synapseMsgContext Synapse Message Context of incoming message * @return true if the message should be routed, false otherwise */ private boolean isAllowedAxis2Path(org.apache.synapse.MessageContext synapseMsgContext) { boolean isProxy = false; String reqUri = request.getUri(); String tenant = MultitenantUtils.getTenantDomainFromUrl(request.getUri()); String servicePath = getSourceConfiguration().getConfigurationContext().getServicePath(); //for tenants, service path will be appended by tenant name if (!reqUri.equalsIgnoreCase(tenant)) { servicePath = servicePath + "/t/" + tenant; } //Get the operation part from the request URL // e.g. '/services/TestProxy/' > TestProxy when service path is '/service/' > result 'TestProxy/' String serviceOpPart = Utils.getServiceAndOperationPart(reqUri, servicePath); //if proxy, then check whether it is deployed in the environment if (serviceOpPart != null) { isProxy = isProxyDeployed(synapseMsgContext, serviceOpPart); } else { if (log.isDebugEnabled()) { log.debug("Requested Proxy Service '" + serviceOpPart + "' is not deployed"); } } return isProxy; } /** * Checks whether the given proxy is deployed in synapse environment * * @param synapseContext Synapse Message Context of incoming message * @param serviceOpPart String name of the service operation * @return true if the proxy is deployed, false otherwise */ private boolean isProxyDeployed(org.apache.synapse.MessageContext synapseContext, String serviceOpPart) { boolean isDeployed = false; //extract proxy name from serviceOperation, get the first portion split by '/' String proxyName = serviceOpPart.split("/")[0]; //check whether the proxy is deployed in synapse environment if (synapseContext.getConfiguration().getProxyService(proxyName) != null) { isDeployed = true; } return isDeployed; } /** * Creates synapse message context from axis2 context * * @param inboundSourceRequest Source Request of inbound * @param axis2Context Axis2 message context of message * @return Synapse Message Context instance * @throws AxisFault */ private org.apache.synapse.MessageContext createSynapseMessageContext( SourceRequest inboundSourceRequest, MessageContext axis2Context) throws AxisFault { // Create super tenant message context MessageContext axis2MsgCtx = axis2Context; // If not super tenant, assign tenant configuration context if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { try { // We have to start the tenant flow inorder to get the tenant from the tenant cache. ConfigurationContext tenantConfigCtx = null; try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext .getThreadLocalCarbonContext(); privilegedCarbonContext.setTenantDomain(tenantDomain, true); tenantConfigCtx = TenantAxisUtils.getTenantConfigurationContext(tenantDomain, axis2MsgCtx.getConfigurationContext()); } finally { PrivilegedCarbonContext.endTenantFlow(); } axis2MsgCtx.setConfigurationContext(tenantConfigCtx); axis2MsgCtx.setProperty(MultitenantConstants.TENANT_DOMAIN, tenantDomain); } catch (Exception e) { log.warn("Could not get tenant configuration context for tenant " + tenantDomain + ". " + "Tenant may not exist. Message will be dispatched to super tenant."); tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; } } return MessageContextCreatorForAxis2.getSynapseMessageContext(axis2MsgCtx); } /** * Updates additional properties in Axis2 Message Context from Synapse Message Context * * @param synCtx Synapse Message Context * @return Updated Axis2 Message Context * @throws AxisFault */ private org.apache.synapse.MessageContext updateAxis2MessageContextForSynapse( org.apache.synapse.MessageContext synCtx) throws AxisFault { ServiceContext svcCtx = new ServiceContext(); OperationContext opCtx = new OperationContext(new InOutAxisOperation(), svcCtx); ((Axis2MessageContext) synCtx).getAxis2MessageContext().setServiceContext(svcCtx); ((Axis2MessageContext) synCtx).getAxis2MessageContext().setOperationContext(opCtx); return synCtx; } }