Package org.apache.ws.resource.handler

Source Code of org.apache.ws.resource.handler.ResourceHandler

/*=============================================================================*
*  Copyright 2004 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.apache.ws.resource.handler;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.Soap1_1Constants;
import org.apache.ws.resource.ResourceContext;
import org.apache.ws.resource.ResourceContextException;
import org.apache.ws.resource.faults.FaultException;
import org.apache.ws.resource.i18n.Keys;
import org.apache.ws.resource.i18n.MessagesImpl;
import org.apache.ws.util.XmlBeanUtils;
import org.apache.ws.util.i18n.Messages;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;

import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.GenericHandler;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.rpc.soap.SOAPFaultException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Base class for platform-specific providers; also is a JAX-RPC Handler to make it easier to port to different SOAP
* platforms.
*
* @author Ian Springer
* @author Sal Campana
*/
public abstract class ResourceHandler
        extends GenericHandler
{
    private static final Log LOG = LogFactory.getLog( ResourceHandler.class );

    /**
     * DOCUMENT_ME
     */
    public static final Messages MSG = MessagesImpl.getInstance();

    /**
     * DOCUMENT_ME
     */
    private static final String WSRF_RESPONSE_XMLOBJECT_LIST = "WSRF_RESPONSE_XMLOBJECT_LIST";

    /**
     * DOCUMENT_ME
     */
    public static final String SERVICE_OPT_WSDL_TARGET_NAMESPACE = "wsdlTargetNamespace";

    /**
     * DOCUMENT_ME
     */
    public static final String SERVICE_OPT_SERVICE_CLASS_NAME = "serviceClassName";

    public static final String HANDLER_OPT_VALIDATE_REQUEST_XML = "validateRequestXml";

    static
    {
        ExceptionUtils.addCauseMethodName( "getLinkedCause" ); // for JAXRPCException
    }

    private Map m_handlerOptions;

    public void init( HandlerInfo handlerInfo )
    {
        Map handlerConfig = handlerInfo.getHandlerConfig();
        m_handlerOptions = handlerConfig != null ? handlerConfig : new HashMap();
    }

    /**
     * This handler acts as the pivot and does not process any SOAP header elements.
     *
     * @return the names of the SOAP header elements that this handler processes
     */
    public QName[] getHeaders()
    {
        return new QName[0];
    }

    /**
     * Deserializes the incoming SOAP request to an XMLBean and dispatches it to the appropriate service. It is required
     * that the request meet the following criteria: <ol> <li>SOAP Header contains the header elements required by
     * WS-Addressing (either 2003/03 or 2004/08)</li> <li>SOAP Body contains no more than one body element</li> </ol>
     */
    public boolean handleRequest( MessageContext msgContext )
    {
        try
        {
            LOG.debug( MSG.getMessage( Keys.RECEIVED_REQUEST ) );
            SOAPMessageContext soapMsgContext = (SOAPMessageContext) msgContext;
            SOAPEnvelope envelope = soapMsgContext.getMessage().getSOAPPart().getEnvelope();
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( "Received SOAP request: \n" + envelope );
            }
            org.apache.ws.util.soap.Body body = getSoapBody( envelope );

            ResourceContext resourceContext = createResourceContext( soapMsgContext );
            String addressingAction = resourceContext.getAddressingAction();
            SoapMethodNameMap methodnameMap = new ServiceSoapMethodNameMap( resourceContext );
            String methodNameFromAction = methodnameMap.getMethodNameFromAction( addressingAction );
            List responseBodyElems = new ArrayList();

            Class serviceClass = getServiceClassName( resourceContext );
            WsrfService service = createService( serviceClass, resourceContext );
            XmlObject[] childElems = XmlBeanUtils.getChildElements( body.getXmlObject() );
            Method serviceMethod = null;

            if ( childElems.length > 1 )
            {
                throw new FaultException( Soap1_1Constants.FAULT_CLIENT,
                        "SOAP request Body contains more than one body element - this service requires that all SOAP requests contain at most one body element." );
            }
            // TODO: refactor below if-else - too much code duplication
            if ( childElems.length == 0 // empty Body
            {
                if ( methodNameFromAction != null )
                {
                    // try to find a method based on the wsa:Action...
                    serviceMethod = getServiceMethod( service, methodNameFromAction );
                    LOG.debug( MSG.getMessage( Keys.INVOKE_RESOURCE_METHOD,
                            serviceMethod.getName() ) );
                    XmlObject responseXBean = invokeServiceMethod( serviceMethod, service, null, serviceClass );
                    if ( responseXBean != null )
                    {
                        responseBodyElems.add( responseXBean );
                    }
                }
                else // empty Body and unmapped wsa:Action
                {
                    throw new FaultException( Soap1_1Constants.FAULT_CLIENT,
                            "No SOAP Body elements were defined, and the value of the WS-Addressing Action header was not recognized - unable to dispatch request." );
                }
            }
            else    // childElems.length == 1
            {
                XmlObject requestXBean = toDocumentXmlBean( childElems[0] );
                validateRequestXml( requestXBean );
                if ( methodNameFromAction != null )
                {
                    serviceMethod = getServiceMethod( service, methodNameFromAction, requestXBean ); //get method based on Action
                }
                else
                {
                    serviceMethod = getServiceMethod( service, requestXBean ); //get method based solely on request elem
                }
                LOG.debug( MSG.getMessage( Keys.INVOKE_RESOURCE_METHOD,
                        serviceMethod.getName() ) );
                XmlObject responseXBean = invokeServiceMethod( serviceMethod, service, requestXBean, serviceClass );
                if ( responseXBean != null )
                {
                    responseBodyElems.add( responseXBean );
                }
            }
            if ( !responseBodyElems.isEmpty() )
            {
                msgContext.setProperty( WSRF_RESPONSE_XMLOBJECT_LIST, responseBodyElems );
            }
        }
        catch ( Exception e )
        {
            handleException( e );
        }
        return false// short-circuit any remaining Handlers in chain
    }

    /**
     * DOCUMENT_ME
     *
     * @param messageContext DOCUMENT_ME
     *
     * @return DOCUMENT_ME
     */
    public boolean handleResponse( MessageContext messageContext )
    {
        try
        {
            LOG.debug( MSG.getMessage( Keys.HANDLING_RESPONSE ) );
            SOAPEnvelope responseEnvelope = getResponseEnvelope( (SOAPMessageContext) messageContext );
            List responseBodyElems = (List) messageContext.getProperty( WSRF_RESPONSE_XMLOBJECT_LIST );
            LOG.debug( MSG.getMessage( Keys.FOUND_RESP_ELEMS, Integer.toString( responseBodyElems.size() ) ) );
            if ( responseBodyElems != null )
            {
                SOAPBody responseBody = responseEnvelope.getBody();
                for ( int i = 0; i < responseBodyElems.size(); i++ )
                {
                    XmlObject responseBodyElem = (XmlObject) responseBodyElems.get( i );
                    SOAPBodyElement[] soapBodyElements = createSOAPBodyElements( responseBodyElem );
                    for ( int j = 0; j < soapBodyElements.length; j++ )
                    {
                        SOAPBodyElement soapBodyElement = soapBodyElements[j];
                        responseBody.addChildElement( soapBodyElement );
                    }
                }
            }
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( "Sending SOAP response: \n" + responseEnvelope );
            }
        }
        catch ( Exception e )
        {
            handleException( e );
        }
        return false// short-circuit any remaining Handlers in chain
    }

    /**
     * Creates a {@link ResourceContext} for this request.
     *
     * @param soapMsgContext the JAX-RPC SOAP message context for this request
     *
     * @return a ResourceContext for this request
     */
    protected abstract ResourceContext createResourceContext( SOAPMessageContext soapMsgContext );

    /**
     * DOCUMENT_ME
     *
     * @param responseBodyElem DOCUMENT_ME
     *
     * @return DOCUMENT_ME
     */
    protected abstract SOAPBodyElement[] createSOAPBodyElements( XmlObject responseBodyElem );

    /**
     * DOCUMENT_ME
     *
     * @param resourceContext DOCUMENT_ME
     *
     * @return DOCUMENT_ME
     */
    protected Class getServiceClassName( ResourceContext resourceContext )
            throws ResourceContextException,
            ClassNotFoundException
    {
        String serviceClassName = resourceContext.getResourceHome().getServiceClassName();
        LOG.debug( MSG.getMessage( Keys.RETRIEVED_SERVICE_CLASSNAME, serviceClassName ) );
        if ( serviceClassName == null )
        {
            throw new IllegalStateException( MSG.getMessage( Keys.SERVICE_OPT_UNDEFINED_IN_HOME,
                    SERVICE_OPT_SERVICE_CLASS_NAME ) );
        }
        return Class.forName( serviceClassName );
    }

    /**
     * @param resourceContext
     * @param key
     *
     * @return the service option string
     */
    protected final String getServiceOption( ResourceContext resourceContext,
                                             String key )
    {
        return (String) resourceContext.getProperty( key );
    }

    /**
     * DOCUMENT_ME
     *
     * @param soapMsgContext DOCUMENT_ME
     *
     * @return DOCUMENT_ME
     */
    protected SOAPEnvelope getResponseEnvelope( SOAPMessageContext soapMsgContext )
    {
        return ( getEnvelope( getResponseMessage( soapMsgContext ) ) );
    }

    /**
     * @param soapMsgContext the response's JAX-RPC message context
     *
     * @return response JAX-RPC SOAP message
     */
    protected SOAPMessage getResponseMessage( SOAPMessageContext soapMsgContext )
    {
        SOAPMessage soapMsg = soapMsgContext.getMessage();
        if ( soapMsg == null )
        {
            soapMsg = createSOAPMessage();
            soapMsgContext.setMessage( soapMsg );
        }

        return ( soapMsg );
    }

    private void validateRequestXml( XmlObject requestXBean )
    {
        boolean validateRequestXml = Boolean.valueOf( getHandlerOption( HANDLER_OPT_VALIDATE_REQUEST_XML, "true" ) )
                .booleanValue();
        if ( validateRequestXml && !( requestXBean instanceof XmlAnyTypeImpl ) )
        {
            XmlOptions validateOptions = new XmlOptions();
            List errorList = new ArrayList();
            validateOptions.setErrorListener( errorList );
            boolean isValid = requestXBean.validate( validateOptions );
            if ( !isValid )
            {
                QName bodyElemName = XmlBeanUtils.getName( requestXBean );
                StringBuffer strBuf = new StringBuffer( "Request body element " );
                strBuf.append( toString( bodyElemName ) );
                strBuf.append( " is not valid as per its schema: \n\n" );
                for ( int i = 0; i < errorList.size(); i++ )
                {
                    strBuf.append( "\t\t" );
                    strBuf.append( i + 1 );
                    strBuf.append( ") " );
                    strBuf.append( errorList.get( i ) );
                    strBuf.append( "\n" );
                }
                strBuf.append( "\n" );
                throw new FaultException( Soap1_1Constants.FAULT_CLIENT, strBuf.toString() );
            }
        }
    }

    private void handleException( Exception e )
    {
        if ( e instanceof SOAPFaultException )
        {
            throw (SOAPFaultException) e;
        }
        else
        {
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( MSG.getMessage( Keys.INTERNAL_SERVER_ERROR ) );
                e.printStackTrace();
            }
            throw new FaultException( Soap1_1Constants.FAULT_SERVER, MSG.getMessage( Keys.INTERNAL_SERVER_ERROR ) );
        }
    }

    private XmlObject toDocumentXmlBean( XmlObject xBean )
            throws XmlException
    {
        // TODO: probably should change method signatures to take types instead of documents to avoid this
        return XmlObject.Factory.parse( xBean.xmlText( new XmlOptions().setSaveOuter() ) );
    }

    /**
     * Returns a facade-wrapped SOAPBody
     *
     * @param envelope
     *
     * @return a facade-wrapped SOAPBody
     *
     * @throws XmlException
     */
    private org.apache.ws.util.soap.Body getSoapBody( SOAPEnvelope envelope ) throws XmlException
    {
        org.apache.ws.util.soap.Envelope envelopeWrapper = null;
        XmlObject envelopeDocXmlBean = XmlObject.Factory.parse( envelope.toString() );
        if ( envelopeDocXmlBean instanceof org.w3.x2003.x05.soapEnvelope.EnvelopeDocument )
        {
            envelopeWrapper =
                    new org.apache.ws.util.soap.Envelope(
                            (org.w3.x2003.x05.soapEnvelope.EnvelopeDocument) envelopeDocXmlBean );
        }
        else if ( envelopeDocXmlBean instanceof org.xmlsoap.schemas.soap.envelope.EnvelopeDocument )
        {
            envelopeWrapper =
                    new org.apache.ws.util.soap.Envelope(
                            (org.xmlsoap.schemas.soap.envelope.EnvelopeDocument) envelopeDocXmlBean );
        }
        else
        {
            throw new IllegalArgumentException(
                    "Unknown version of SOAPEnvelope: " + envelopeDocXmlBean.getClass().getName() );
        }

        return envelopeWrapper.getBody();
    }

    /**
     * This method is used when there are no parameters to a method
     *
     * @param service              The service to find the method on.
     * @param methodNameFromAction The method name we are looking for.
     *
     * @return The Method object
     */
    private Method getServiceMethod( WsrfService service, String methodNameFromAction )
    {
        Method serviceMethod = null;
        LOG.debug( "Based on the request, looking for method named: " + methodNameFromAction + " in service " +
                service.getClass().getName() +
                " with " +
                0 +
                " param types" );
        Method[] methods = service.getClass().getMethods();
        for ( int i = 0; i < methods.length; i++ )
        {
            Method method = methods[i];
            if ( method.getName().equals( methodNameFromAction ) )
            {
                if ( method.getParameterTypes().length == 0 )
                {
                    serviceMethod = method;
                    break;
                }
                else
                {
                    LOG.warn( "Found method named: " + methodNameFromAction + " in service " +
                            service.getClass().getName() +
                            " with " +
                            method.getParameterTypes().length +
                            " param types, expected 0 param type." );
                }
            }
        }

        if ( serviceMethod == null )
        {
            throw new RuntimeException( MSG.getMessage( Keys.BAD_REQUEST_BODY_ELEMENT,
                    methodNameFromAction,
                    service.getClass().getName() ) );
        }

        LOG.debug( MSG.getMessage( Keys.FOUND_SERVICE_METHOD, serviceMethod.getName() ) );
        return serviceMethod;
    }

    private XmlObject invokeServiceMethod( Method serviceMethod, WsrfService service, XmlObject requestXBean,
                                           Class serviceClass )
            throws Exception
    {
        XmlObject responseXBean = null;
        Object[] params = ( requestXBean != null ) ? new Object[]
        {
            requestXBean
        } : new Object[0];
        try
        {
            responseXBean = (XmlObject) serviceMethod.invoke( service, params );
        }
        catch ( InvocationTargetException ite )
        {
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( MSG.getMessage( Keys.ERROR_INVOKING_METHOD_ON_SERVICE,
                    serviceMethod.getName(),
                    serviceClass.getName() ) );
                if ( ite.getCause() != null )
                {
                    ite.getCause().printStackTrace( );
                }
            }
            if ( ite.getCause() != null )
            {
                throw (Exception) ite.getCause();
            }
            else
            {
                throw ite;
            }
        }
        if ( responseXBean == null && serviceMethod.getReturnType() != void.class )
        {
            // don't allow service method to return null
            LOG.error( "Service method " + serviceMethod.getName() + " in class " + serviceClass.getName() + " returned null - this is not allowed." );
            throw new IllegalStateException();
        }
        return responseXBean;
    }

    /**
     * @param soapMsg a SAAJ SOAP message
     *
     * @return
     */
    private SOAPEnvelope getEnvelope( SOAPMessage soapMsg )
    {
        try
        {
            return soapMsg.getSOAPPart().getEnvelope();
        }
        catch ( SOAPException soape )
        {
            throw new JAXRPCException( "Failed to get SOAPEnvelope from request SOAPMessage.", soape );
        }
    }

    /**
     * Finds the method based on the requestXBean name
     *
     * @param service
     * @param requestXBean
     *
     * @return
     */
    private Method getServiceMethod( WsrfService service,
                                     XmlObject requestXBean )
    {
        QName bodyElemName = XmlBeanUtils.getName( requestXBean );
        if ( LOG.isDebugEnabled() )
        {
            LOG.debug( MSG.getMessage( Keys.DERIVE_SERVICE_NAME_FROM_REQ, toString( bodyElemName ) ) );
        }
        String serviceMethodName = service.getMethodNameMap().getMethodName( bodyElemName );
        return getServiceMethod( service, serviceMethodName, requestXBean );
    }

    /**
     * Method used to locate a Method object in a Service based on the method name and param
     *
     * @param service
     * @param methodName
     * @param param
     *
     * @return
     */
    private Method getServiceMethod( WsrfService service, String methodName, XmlObject param )
    {
        Method serviceMethod = null;
        LOG.debug( "Based on the request, looking for method named: " + methodName + " in service " +
                service.getClass().getName() +
                " with " +
                1 +
                " param type called: " +
                param ==
                null ?
                "null" : param.getClass().getName() );
        Method[] methods = service.getClass().getMethods();
        for ( int i = 0; i < methods.length; i++ )
        {
            Method method = methods[i];
            if ( method.getName().equals( methodName ) )
            {
                if ( method.getParameterTypes().length == 1 )
                {
                    if ( method.getParameterTypes()[0].isInstance( param ) )   //todo check if handles null
                    {
                        serviceMethod = method;
                        break;
                    }
                    else
                    {
                        LOG.warn( "Found method named: " + methodName + " in service " +
                                service.getClass().getName() +
                                " with " +
                                1 +
                                " param of type: " +
                                method.getParameterTypes()[0].getName() +
                                " , however the request param was: " +
                                param.getClass().getName() );
                    }
                }
                else
                {
                    LOG.warn( "Found method named: " + methodName + " in service " +
                            service.getClass().getName() +
                            " with " +
                            method.getParameterTypes().length +
                            " param types, expected 1 param type." );
                }
            }
        }
        if ( serviceMethod == null // method not found
        {
            QName bodyElemName = XmlBeanUtils.getName( param );
            throw new FaultException( Soap1_1Constants.FAULT_CLIENT,
                    MSG.getMessage( Keys.BAD_REQUEST_BODY_ELEMENT, toString( bodyElemName ) ) );
        }
        LOG.debug( MSG.getMessage( Keys.FOUND_SERVICE_METHOD, serviceMethod.getName() ) );
        return serviceMethod;
    }

    private static String toString( QName name )
    {
        StringBuffer strBuf = new StringBuffer();
        strBuf.append( name.getLocalPart() );
        if ( name.getNamespaceURI() != null )
        {
            strBuf.append( "@" );
            strBuf.append( name.getNamespaceURI() );
        }
        return strBuf.toString();
    }

    private SOAPMessage createSOAPMessage()
    {
        try
        {
            return MessageFactory.newInstance().createMessage();
        }
        catch ( SOAPException soape )
        {
            throw new JAXRPCException( MSG.getMessage( Keys.FAILED_TO_CREATE_SOAPMESSAGE ), soape );
        }
    }

    private WsrfService createService( Class serviceClass,
                                       ResourceContext resourceContext )
            throws Exception
    {
        LOG.debug( MSG.getMessage( Keys.CREATING_INSTANCE_OF_SERVICE, serviceClass ) );
        Constructor serviceCtor = serviceClass.getConstructor( new Class[]
        {
            ResourceContext.class
        } );
        WsrfService service = null;
        try
        {
            service = (WsrfService) serviceCtor.newInstance( new Object[]
            {
                resourceContext
            } );
        }
        catch ( InvocationTargetException ite )
        {
            throw (Exception) ite.getCause();
        }
        return service;
    }

    private String getHandlerOption( String optionName, String defaultValue )
    {
        Object value = m_handlerOptions != null ? m_handlerOptions.get( optionName ) : null;
        return value != null ? value.toString() : defaultValue;
    }
}
TOP

Related Classes of org.apache.ws.resource.handler.ResourceHandler

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.