Package org.wso2.carbon.core.multitenancy

Source Code of org.wso2.carbon.core.multitenancy.MultitenantMessageReceiver

/*                                                                            
* Copyright 2004,2005 The Apache Software Foundation.                        
*                                                                            
* Licensed 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.core.multitenancy;

import org.apache.axiom.attachments.Attachments;
import org.apache.axiom.om.util.UUIDGenerator;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.OperationContext;
import org.apache.axis2.description.TransportInDescription;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.description.WSDL2Constants;
import org.apache.axis2.engine.AxisEngine;
import org.apache.axis2.engine.MessageReceiver;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.transport.http.util.RESTUtil;

import org.apache.axis2.util.MessageContextBuilder;
import org.apache.axis2.util.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;

/**
* This MessageReceiver will try to locate the tenant specific AxisConfiguration and dispatch the
* request to that AxisConfiguration. Dispatching to the actual service & operation will happen
* within the tenant specific AxisConfiguration.
*/
public class MultitenantMessageReceiver implements MessageReceiver {

    private static final Log log = LogFactory.getLog(MultitenantMessageReceiver.class);                     

    public void receive(MessageContext mainInMsgContext) throws AxisFault {

        EndpointReference toEpr = getDestinationEPR(mainInMsgContext);
        if (toEpr != null) {
            // this is a request coming in to the multitenant environment
            processRequest(mainInMsgContext);
        } else {
            // this is a response coming from a dual channel invocation or a
            // non blocking transport like esb
            processResponse(mainInMsgContext);
        }
    }

    /**
     * Process a response message coming in the multitenant environment
     *
     * @param mainInMsgContext supertenant MessageContext
     * @throws AxisFault if an error occurs
     */
    private void processResponse(MessageContext mainInMsgContext) throws AxisFault {
        MessageContext requestMsgCtx = mainInMsgContext.getOperationContext().
                getMessageContext(WSDL2Constants.MESSAGE_LABEL_IN);
        if (requestMsgCtx != null) {
            MessageContext tenantRequestMsgCtx = (MessageContext)
                    requestMsgCtx.getProperty(MultitenantConstants.TENANT_REQUEST_MSG_CTX);

            if (tenantRequestMsgCtx != null) {
                MessageContext tenantResponseMsgCtx = tenantRequestMsgCtx.getOperationContext().
                        getMessageContext(WSDL2Constants.MESSAGE_LABEL_IN);

                if (tenantResponseMsgCtx == null) {
                    tenantResponseMsgCtx = new MessageContext();
                    tenantResponseMsgCtx.setOperationContext(tenantRequestMsgCtx.getOperationContext());
                }

                tenantResponseMsgCtx.setServerSide(true);
                tenantResponseMsgCtx.setDoingREST(tenantRequestMsgCtx.isDoingREST());
                tenantResponseMsgCtx.setProperty(MessageContext.TRANSPORT_IN, tenantRequestMsgCtx
                        .getProperty(MessageContext.TRANSPORT_IN));
                tenantResponseMsgCtx.setTransportIn(tenantRequestMsgCtx.getTransportIn());
                tenantResponseMsgCtx.setTransportOut(tenantRequestMsgCtx.getTransportOut());

                tenantResponseMsgCtx.setProperty(MessageContext.TRANSPORT_HEADERS,
                        mainInMsgContext.getProperty(MessageContext.TRANSPORT_HEADERS));

                tenantResponseMsgCtx.setAxisMessage(
                        tenantRequestMsgCtx.getOperationContext().getAxisOperation().
                        getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE));

                tenantResponseMsgCtx.setOperationContext(tenantRequestMsgCtx.getOperationContext());
                tenantResponseMsgCtx.setConfigurationContext(
                        tenantRequestMsgCtx.getConfigurationContext());

                tenantResponseMsgCtx.setTo(null);

                tenantResponseMsgCtx.setEnvelope(mainInMsgContext.getEnvelope());

                AxisEngine.receive(tenantResponseMsgCtx);
            }
        }
    }

    /**
     * Process a request message coming in to the multitenant environment
     *
     * @param mainInMsgContext super tenant's MessageContext
     * @throws AxisFault if an error occurs
     */
    private void processRequest(MessageContext mainInMsgContext) throws AxisFault {
        ConfigurationContext mainConfigCtx = mainInMsgContext.getConfigurationContext();
        String to = mainInMsgContext.getTo().getAddress();
        int tenantDelimiterIndex = to.indexOf("/t/");

        String tenant;
        String serviceAndOperation;

        if (tenantDelimiterIndex != -1) {
            tenant = MultitenantUtils.getTenantDomainFromUrl(to);
            serviceAndOperation = to.substring(tenantDelimiterIndex + tenant.length() + 4);
        } else {
            // in this case tenant detail is not with the url but user may have send it
            // with a soap header or an http header.
            // in that case TenantDomainHandler may have set it.
            tenant = SuperTenantCarbonContext.getCurrentContext(mainInMsgContext).getTenantDomain();
            serviceAndOperation = Utils.getServiceAndOperationPart(to,
                    mainInMsgContext.getConfigurationContext().getServiceContextPath());
        }

        if (tenant == null) {
            // Throw an AxisFault: Tenant not specified
            handleException(mainInMsgContext, new AxisFault("Tenant not specified"));
            return;
        }

        // this is to prevent non-blocking transports from sending 202
        mainInMsgContext.getOperationContext().setProperty(
                    Constants.RESPONSE_WRITTEN, "SKIP");

        ConfigurationContext tenantConfigCtx =
                TenantAxisUtils.getTenantConfigurationContext(tenant, mainConfigCtx);
        if (tenantConfigCtx == null) {
            // Throw AxisFault: Tenant does not exist
            handleException(mainInMsgContext, new AxisFault("Tenant " + tenant + "  not found"));
            return;
        }

        if (mainInMsgContext.isDoingREST()) { // Handle REST requests
            doREST(mainInMsgContext, to, tenant, tenantConfigCtx, serviceAndOperation);
        } else {
            doSOAP(mainInMsgContext, tenant, tenantConfigCtx, serviceAndOperation);
        }
    }

    /**
     * Process a SOAP request coming in to the multitenant environment
     * @param mainInMsgContext super tenant's message context
     * @param tenant nameof the tenant
     * @param tenantConfigCtx tenant's ConfigurationContext
     * @param serviceName name of the service
     * @throws AxisFault if an error occurs
     */
    private void doSOAP(MessageContext mainInMsgContext,
                        String tenant,
                        ConfigurationContext tenantConfigCtx,
                        String serviceName) throws AxisFault {

        // Call the correct tenant's configuration
        MessageContext tenantInMsgCtx = tenantConfigCtx.createMessageContext();
        tenantInMsgCtx.setMessageID(UUIDGenerator.getUUID());
        Options options = tenantInMsgCtx.getOptions();

        options.setTo(new EndpointReference("local://" + tenantConfigCtx.getServicePath() +
                "/" + serviceName));
        options.setAction(mainInMsgContext.getSoapAction());

        tenantInMsgCtx.setEnvelope(mainInMsgContext.getEnvelope());

        tenantInMsgCtx.setServerSide(true);
        copyProperties(mainInMsgContext, tenantInMsgCtx);

        SuperTenantCarbonContext.getCurrentContext(tenantInMsgCtx).setTenantDomain(tenant, true);
        try {
            // set a dummy transport out description
            String transportOutName = mainInMsgContext.getTransportOut().getName();
            TransportOutDescription transportOutDescription =
                    tenantConfigCtx.getAxisConfiguration().getTransportOut(transportOutName);
            tenantInMsgCtx.setTransportOut(transportOutDescription);
            TransportInDescription incomingTransport =
                    tenantConfigCtx.getAxisConfiguration().
                            getTransportIn(mainInMsgContext.getIncomingTransportName());
            tenantInMsgCtx.setTransportIn(incomingTransport);

            tenantInMsgCtx.setProperty(MessageContext.TRANSPORT_OUT,
                    mainInMsgContext.getProperty(MessageContext.TRANSPORT_OUT));
            tenantInMsgCtx.setProperty(Constants.OUT_TRANSPORT_INFO,
                    mainInMsgContext.getProperty(Constants.OUT_TRANSPORT_INFO));
            tenantInMsgCtx.setIncomingTransportName(mainInMsgContext.getIncomingTransportName());

            // inject the message to the tenant inflow handlers
            AxisEngine.receive(tenantInMsgCtx);
        } catch (AxisFault axisFault) {
            // at a fault flow message receiver throws a fault.
            // we need to first catch this fault and invoke the fault flow
            // then thorw the AxisFault for main flow which is catch by the carbon servlet
            // and invoke the fault handlers.

            MessageContext faultContext =
                    MessageContextBuilder.createFaultMessageContext(tenantInMsgCtx, axisFault);
            faultContext.setTransportOut(tenantInMsgCtx.getTransportOut());
            faultContext.setProperty(MultitenantConstants.TENANT_MR_STARTED_FAULT,
                    Constants.VALUE_TRUE);
            AxisEngine.sendFault(faultContext);       

            // we need to set the detial to null. otherwise the details element is copied to
            // new message context and removed from the original one
            axisFault.setDetail(null);
            MessageContext mainFaultContext = MessageContextBuilder.createFaultMessageContext(
                    mainInMsgContext, axisFault);
            mainFaultContext.setTo(faultContext.getTo());
            mainFaultContext.setSoapAction(faultContext.getSoapAction());

            mainFaultContext.setEnvelope(faultContext.getEnvelope());
            throw new AxisFault(axisFault.getMessage(), mainFaultContext);

        }
    }

    /**
     * Process a REST message coming in to the multitenant environment
     * @param mainInMsgContext SuperTenant's Message Context
     * @param to the to address
     * @param tenant tenant name
     * @param tenantConfigCtx Tentnat's configuration context
     * @param serviceName service name
     * @throws AxisFault if an error occurs
     */
    private void doREST(MessageContext mainInMsgContext,
                        String to,
                        String tenant,
                        ConfigurationContext tenantConfigCtx,
                        String serviceName) throws AxisFault {
        HttpServletRequest request =
                (HttpServletRequest) mainInMsgContext.getProperty(
                        HTTPConstants.MC_HTTP_SERVLETREQUEST);

        if (request != null) {
            HttpServletResponse response =
                (HttpServletResponse) mainInMsgContext.getProperty(
                        HTTPConstants.MC_HTTP_SERVLETRESPONSE);
            doServletRest(mainInMsgContext, to, tenant, tenantConfigCtx,
                    serviceName, request, response);
        } else {
            doNhttpREST(mainInMsgContext, to, tenant,
                    tenantConfigCtx, serviceName);
        }
    }

    /**
     * Process a REST message coming in from the Servlet transport.
     * @param mainInMsgContext supertenant's MessageContext
     * @param to the full transport url
     * @param tenant name of the tenant
     * @param tenantConfigCtx tenant ConfigurationContext
     * @param serviceName the part of the to url after the service
     * @param request servlet request
     * @param response servlet response
     * @throws AxisFault if an error occcus
     */
    private void doServletRest(MessageContext mainInMsgContext, String to,
                               String tenant, ConfigurationContext tenantConfigCtx,
                               String serviceName, HttpServletRequest request,
                               HttpServletResponse response) throws AxisFault {
        String serviceWithSlashT = "/t/" + tenant + "/" + serviceName;
        String requestUri = "local://" + tenantConfigCtx.getServicePath() + "/" + serviceName +
                (to.endsWith(serviceWithSlashT) ?
                        "" :
                        "/" + to.substring(to.indexOf(serviceWithSlashT) +
                                serviceWithSlashT.length() + 1));

        MultitenantRESTServlet restServlet = new MultitenantRESTServlet(
                tenantConfigCtx, requestUri, tenant);

        String httpMethod = (String) mainInMsgContext.getProperty(HTTPConstants.HTTP_METHOD);
        try {
            if (httpMethod.equals(Constants.Configuration.HTTP_METHOD_GET)) {
                restServlet.doGet(request, response);
            } else if (httpMethod.equals(Constants.Configuration.HTTP_METHOD_POST)) {
                restServlet.doPost(request, response);
            } else if (httpMethod.equals(Constants.Configuration.HTTP_METHOD_PUT)) {
                restServlet.doPut(request, response);
            } else if (httpMethod.equals(Constants.Configuration.HTTP_METHOD_DELETE)) {
                restServlet.doDelete(request, response);
            } else {
                // TODO: throw exception: Invalid verb
            }
        } catch (ServletException e) {
            throw new AxisFault(e.getMessage());
        } catch (IOException e) {
            throw new AxisFault(e.getMessage());
        }

        // Send the response
        MessageContext tenantOutMsgContext = restServlet.getOutMessageContext();
        MessageContext tenantOutFaultMsgContext = restServlet.getOutFaultMessageContext();

        // for a fault case both out and fault contexts are not null. so first we need to
        // check the fault context
        if (tenantOutFaultMsgContext != null) {
            // TODO: set the fault exception
            MessageContext mainOutFaultMsgContext =
                    MessageContextBuilder.createFaultMessageContext(mainInMsgContext, null);
            mainOutFaultMsgContext.setEnvelope(tenantOutFaultMsgContext.getEnvelope());
            throw new AxisFault("Problem with executing the message", mainOutFaultMsgContext);
        } else if (tenantOutMsgContext != null) {
            MessageContext mainOutMsgContext =
                    MessageContextBuilder.createOutMessageContext(mainInMsgContext);
            mainOutMsgContext.getOperationContext().addMessageContext(mainOutMsgContext);
            mainOutMsgContext.setEnvelope(tenantOutMsgContext.getEnvelope());
            AxisEngine.send(mainOutMsgContext);
        }
    }

    /**
     * Process a REST message coming in from the NHTTP transport.
     * @param mainInMsgContext supertenant's MessageContext
     * @param to the full transport url
     * @param tenant name of the tenant
     * @param tenantConfigCtx tenant ConfigurationContext
     * @param servicePart the part of the to url after the service
     * @throws AxisFault if an error occcus
     */
    public void doNhttpREST(MessageContext mainInMsgContext,
                        String to,
                        String tenant,
                        ConfigurationContext tenantConfigCtx,
                        String servicePart) throws AxisFault {
        String serviceWithSlashT = "/t/" + tenant + "/" + servicePart;
        String requestUri = "local://" + tenantConfigCtx.getServicePath() + "/" + servicePart +
                (to.endsWith(serviceWithSlashT) ?
                        "" :
                        "/" + to.substring(to.indexOf(serviceWithSlashT) +
                                serviceWithSlashT.length() + 1));
        // Now create the message context to invoke
        MessageContext tenantInMsgCtx = tenantConfigCtx.createMessageContext();

        String trsPrefix = (String) mainInMsgContext.getProperty(
                Constants.Configuration.TRANSPORT_IN_URL);

        int sepindex = trsPrefix.indexOf(':');
        if (sepindex > -1) {
            trsPrefix = trsPrefix.substring(0, sepindex);
            tenantInMsgCtx.setIncomingTransportName(trsPrefix);
        } else {
            tenantInMsgCtx.setIncomingTransportName(Constants.TRANSPORT_HTTP);
        }

        tenantInMsgCtx.setServerSide(true);
        tenantInMsgCtx.setDoingREST(true);
        copyProperties(mainInMsgContext, tenantInMsgCtx);
        tenantInMsgCtx.setTo(new EndpointReference(requestUri));

        // set a dummy transport out description
        String transportOutName = mainInMsgContext.getTransportOut().getName();
        TransportOutDescription transportOutDescription =
                tenantConfigCtx.getAxisConfiguration().getTransportOut(transportOutName);
        tenantInMsgCtx.setTransportOut(transportOutDescription);
        TransportInDescription incomingTransport =
                tenantConfigCtx.getAxisConfiguration().
                        getTransportIn(mainInMsgContext.getIncomingTransportName());
        tenantInMsgCtx.setTransportIn(incomingTransport);

        tenantInMsgCtx.setProperty(MessageContext.TRANSPORT_OUT,
                mainInMsgContext.getProperty(MessageContext.TRANSPORT_OUT));
        tenantInMsgCtx.setProperty(Constants.OUT_TRANSPORT_INFO,
                mainInMsgContext.getProperty(Constants.OUT_TRANSPORT_INFO));
        tenantInMsgCtx.setIncomingTransportName(mainInMsgContext.getIncomingTransportName());

        SuperTenantCarbonContext.getCurrentContext(tenantInMsgCtx).setTenantDomain(tenant, true);

        // extract the part of the user after the actual service and set it as
        int index = servicePart.indexOf('/');
        String service = (index > 0 ?
                servicePart.substring(servicePart.indexOf("/") + 1) : servicePart);       
        String servicePath = "/t/" + tenant + "/" + service;
        String restSuffic = (to.endsWith(servicePath) ? "" :
                to.substring(to.indexOf(servicePath) + servicePath.length() + 1));
        tenantInMsgCtx.setProperty("REST_URL_POSTFIX", restSuffic);

        InputStream in = (InputStream) mainInMsgContext.getProperty("nhttp.input.stream");
        OutputStream os = (OutputStream) mainInMsgContext.getProperty("nhttp.output.stream");
        String contentType = (String) mainInMsgContext.getProperty("ContentType");
        try {
            String httpMethod = (String) mainInMsgContext.getProperty(HTTPConstants.HTTP_METHOD);
            if (httpMethod.equals(Constants.Configuration.HTTP_METHOD_GET) ||
                    httpMethod.equals(Constants.Configuration.HTTP_METHOD_DELETE)) {
                RESTUtil.processURLRequest(tenantInMsgCtx, os, contentType);
            } else if (httpMethod.equals(Constants.Configuration.HTTP_METHOD_POST) ||
                    httpMethod.equals(Constants.Configuration.HTTP_METHOD_PUT)) {
                RESTUtil.processXMLRequest(tenantInMsgCtx, in, os, contentType);
            } else {
                // TODO: throw exception: Invalid verb
            }
        } catch (AxisFault axisFault) {
            // at a fault flow message receiver throws a fault.
            // we need to first catch this fault and invoke the fault flow
            // then thorw the AxisFault for main flow which is catch by the carbon servlet
            // and invoke the fault handlers.

            MessageContext faultContext =
                    MessageContextBuilder.createFaultMessageContext(tenantInMsgCtx, axisFault);
            faultContext.setTransportOut(tenantInMsgCtx.getTransportOut());
            faultContext.setProperty(MultitenantConstants.TENANT_MR_STARTED_FAULT,
                    Constants.VALUE_TRUE);
            AxisEngine.sendFault(faultContext);

            // we need to set the detial to null. otherwise the details element is copied to
            // new message context and removed from the original one
            axisFault.setDetail(null);
            MessageContext mainFaultContext = MessageContextBuilder.
                    createFaultMessageContext(mainInMsgContext, axisFault);
            mainFaultContext.setTo(faultContext.getTo());
            mainFaultContext.setSoapAction(faultContext.getSoapAction());

            mainFaultContext.setEnvelope(faultContext.getEnvelope());
            throw new AxisFault(axisFault.getMessage(), mainFaultContext);
        }       
    }

    /**
     * Copy the properties from main message context to the tenant message context.
     *
     * @param mainMsgCtx super tenant message context
     * @param tenantMsgCtx tenant message context
     */
    private void copyProperties(MessageContext mainMsgCtx, MessageContext tenantMsgCtx) {
        // do not copy options from the original       
        tenantMsgCtx.setSoapAction(mainMsgCtx.getSoapAction());

        tenantMsgCtx.setDoingREST(mainMsgCtx.isDoingREST());
        tenantMsgCtx.setDoingMTOM(mainMsgCtx.isDoingMTOM());
        tenantMsgCtx.setDoingSwA(mainMsgCtx.isDoingSwA());

        // if the original request carries any attachments, copy them to the clone
        // as well, except for the soap part if any
        Attachments attachments = mainMsgCtx.getAttachmentMap();
        if (attachments != null && attachments.getAllContentIDs().length > 0) {
            String[] cIDs = attachments.getAllContentIDs();
            String soapPart = attachments.getSOAPPartContentID();
            for (String cID : cIDs) {
                if (!cID.equals(soapPart)) {
                    tenantMsgCtx.addAttachment(cID, attachments.getDataHandler(cID));
                }
            }
        }

        Iterator itr = mainMsgCtx.getPropertyNames();
        while (itr.hasNext()) {
            String key = (String) itr.next();
            if (key != null) {               
                tenantMsgCtx.setProperty(key, mainMsgCtx.getProperty(key));
            }
        }
    }

    private String getServiceName(String to, int tenantDelimiterIndex, String tenant) {
        String temp = to.substring(tenantDelimiterIndex + 3 + tenant.length() + 1);
        int indexOfSlash = temp.indexOf("/");
        String serviceName = (indexOfSlash != -1) ? temp.substring(0, indexOfSlash) : temp;
        if (log.isDebugEnabled()) {
            log.debug("Service:" + serviceName);
        }
        return serviceName;
    }

    private void handleException(MessageContext mainInMsgContext, AxisFault fault)
            throws AxisFault {
        MessageContext mainOutMsgContext =
                MessageContextBuilder.createFaultMessageContext(mainInMsgContext, fault);
        OperationContext mainOpContext = mainInMsgContext.getOperationContext();
        mainOpContext.addMessageContext(mainOutMsgContext);
        mainOutMsgContext.setOperationContext(mainOpContext);
        AxisEngine.sendFault(mainOutMsgContext);
    }

    /**
     * Get the EPR for the message passed in
     * @param msgContext the message context
     * @return the destination EPR
     */
    public static EndpointReference getDestinationEPR(MessageContext msgContext) {

        // Trasnport URL can be different from the WSA-To
        String transportURL = (String) msgContext.getProperty(
            Constants.Configuration.TRANSPORT_URL);

        if (transportURL != null) {
            return new EndpointReference(transportURL);
        } else if (
            (msgContext.getTo() != null) && !msgContext.getTo().hasAnonymousAddress()) {
            return msgContext.getTo();
        }
        return null;
    }
}
TOP

Related Classes of org.wso2.carbon.core.multitenancy.MultitenantMessageReceiver

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.