Package org.jibx.ws.server

Source Code of org.jibx.ws.server.Service

/*
* Copyright (c) 2007-2009, Sosnoski Software Associates Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of
* JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.jibx.ws.server;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.JiBXException;
import org.jibx.ws.WsBindingException;
import org.jibx.ws.WsConfigurationException;
import org.jibx.ws.WsException;
import org.jibx.ws.context.InContext;
import org.jibx.ws.context.OutContext;
import org.jibx.ws.io.MarshallingPayloadWriter;
import org.jibx.ws.io.UnmarshallingPayloadReader;
import org.jibx.ws.io.XmlOptions;
import org.jibx.ws.process.Processor;
import org.jibx.ws.transport.InConnection;
import org.jibx.ws.transport.OutServerConnection;
import org.jibx.ws.transport.OutputCompletionEvent;
import org.jibx.ws.transport.OutputCompletionListener;
import org.jibx.ws.util.ExpandingPool;
import org.jibx.ws.wsdl.InputStreamWsdlProvider;
import org.jibx.ws.wsdl.WsdlLocationToRequestUrlAdapter;
import org.jibx.ws.wsdl.WsdlProvider;

/**
* Base service implementation for extension by protocol specific implementations.
*
* @author Dennis M. Sosnoski
*/
public abstract class Service
{
    private static final Log logger = LogFactory.getLog(Service.class);

    /** Instance of service class used for processing requests. */
    private final Object m_serviceObj;

    private final ServiceExceptionHandler m_serviceExceptionHandler;
   
    /** Owning pool of instances. */
    private ExpandingPool m_owningPool;
   
    /** Map from input class name (of SOAP body) to {@link OperationDefinition}. */
    private final Map m_operationByBodyMap;

    /** Binding factory for outbound body. */
    private IBindingFactory m_outBodyBindingFactory;

    /** Binding factory for inbound body. */
    private IBindingFactory m_inBodyBindingFactory;

    /** Formatting options for outbound XML. */
    private final XmlOptions m_xmlOptions;

    /** Listener for output completion. */
    private OutputCompletionListener m_outputCompletionListener;

    /** Transport specific options.Map from {@link TransportOptions} class name to {@link TransportOptions} instance. */
    private Map m_transportOptionsMap = Collections.EMPTY_MAP;

    /** Processor for processing the messages. */
    private final Processor m_processor;
   
    private final MediaTypeMapper m_mediaTypeMapper;

    /**
     * Create service from definition.
     *
     * @param sdef service definition information
     * @param processor for processing the message
     * @param mediaTypeMapper to map media type code to media type
     * @param defaultExceptionHandlerFactory for creating exception handler if none defined in service definition
     * @throws WsException on error creating the service
     */
    public Service(ServiceDefinition sdef, Processor processor, MediaTypeMapper mediaTypeMapper,
            ServiceExceptionHandlerFactory defaultExceptionHandlerFactory) throws WsException {
        try {
            m_processor = processor;
            m_mediaTypeMapper = mediaTypeMapper;

            // build map from request body object class to operation
            m_operationByBodyMap = new HashMap();
            Class clas = null;
            boolean hasInputs = false;
            boolean hasOutputs = false;
            for (int i = 0; i < sdef.getOperationDefinitions().size(); i++) {
                OperationDefinition odef = (OperationDefinition) sdef.getOperationDefinitions().get(i);
                Operation op = Operation.newInstance(sdef.getServiceClass(), odef);
                String iname = "";
                if (op.getInputClass() != null) {
                    clas = op.getInputClass();
                    iname = clas.getName();
                    hasInputs = true;
                } else if (clas == null && op.getOutputClass() != null) {
                    clas = op.getOutputClass();
                    hasOutputs = true;
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Adding operation '" + op.toString() + "' to service map with key '" + iname + "'");
                }
                m_operationByBodyMap.put(iname, op);
            }

            if (sdef.getServiceObject() != null) {
                m_serviceObj = sdef.getServiceObject();
            } else {
                // if any of the operation are non static, we need to create a new object for the service
                boolean hasInstanceMethod = false;
                for (Iterator operationIter = m_operationByBodyMap.values().iterator(); operationIter.hasNext();) {
                    Operation operation = (Operation) operationIter.next();
                    if (!operation.isStaticMethod()) {
                        hasInstanceMethod = true;
                        break;
                    }
                }
                if (hasInstanceMethod) {
                    m_serviceObj = sdef.getServiceClass().newInstance();
                } else {
                    m_serviceObj = null;
                }
            }

            if (sdef.getServiceExceptionHandlerObject() != null) {
                m_serviceExceptionHandler = sdef.getServiceExceptionHandlerObject();
            } else if (sdef.getServiceExceptionHandlerClass() != null) {
                m_serviceExceptionHandler =
                    (ServiceExceptionHandler) sdef.getServiceExceptionHandlerClass().newInstance();
            } else {
                m_serviceExceptionHandler = defaultExceptionHandlerFactory.createServiceExceptionHandler();
            }
           
            m_outBodyBindingFactory = sdef.getOutBodyBindingFactory();
            m_inBodyBindingFactory = sdef.getInBodyBindingFactory();

            // set the factory for building marshalling and unmarshalling contexts
            if (m_outBodyBindingFactory == null && m_inBodyBindingFactory == null && clas != null) {
                try {
                    m_outBodyBindingFactory = BindingDirectory.getFactory(clas);
                    m_inBodyBindingFactory = m_outBodyBindingFactory;
                } catch (JiBXException e) {
                    throw new WsConfigurationException(
                        "Unable to create service since bindings not compiled for class " + clas.getName(), e);
                }
            }
            if (hasInputs && (m_inBodyBindingFactory == null)) {
                throw new WsConfigurationException("Binding factories must be defined for body of inbound messages");
            }
            if (hasOutputs && (m_outBodyBindingFactory == null)) {
                throw new WsConfigurationException("Binding factories must be defined for body of outbound messages");
            }
           
            if (sdef.getOutputCompletionListenerObject() != null) {
                m_outputCompletionListener = sdef.getOutputCompletionListenerObject();
            } else if (sdef.getOutputCompletionListenerClass() != null) {
                m_outputCompletionListener =
                    (OutputCompletionListener) sdef.getOutputCompletionListenerClass().newInstance();
            }
           
            m_xmlOptions = sdef.getXmlOptions();

            Collection transportOptionsDefinitions = sdef.getTransportOptionsDefinitions();
            if (transportOptionsDefinitions.size() > 0) {
                m_transportOptionsMap = new HashMap();
                for (Iterator iterator = transportOptionsDefinitions.iterator(); iterator.hasNext();) {
                    TransportOptionsDefinition def = (TransportOptionsDefinition) iterator.next();
                    TransportOptions transportOptions = def.createTransportOptions();
                    m_transportOptionsMap.put(transportOptions.getClass().getName(), transportOptions);
                }
            }
       
            try {
                if (sdef.getWsdlFilepath() != null) {
                    InputStream wsdlStream = Service.class.getResourceAsStream(sdef.getWsdlFilepath());
                    if (wsdlStream == null) {
                        throw new WsConfigurationException("Unable to open WSDL file '" + sdef.getWsdlFilepath() + "'");
                    }
                    WsdlProvider wsdlProvider = new InputStreamWsdlProvider(wsdlStream);
                    if (sdef.getWsdlLocationTransform()) {
                        wsdlProvider = new WsdlLocationToRequestUrlAdapter(wsdlProvider);
                    }
                    setWsdlProvider(wsdlProvider);
                }
            } catch (IOException e) {
                throw new WsConfigurationException("Error reading WSDL file '" + sdef.getWsdlFilepath() + "'");
            }
        } catch (InstantiationException e) {
            throw new WsException("Error creating endpoint service object", e);
        } catch (IllegalAccessException e) {
            throw new WsException("Unable to create endpoint service object", e);
        }
    }

    /**
     * Sets the owning pool for this service. This method must only be called by the owning pool, immediately after
     * construction of the service.
     *
     * @param pool owning pool
     */
    final void setOwningPool(ExpandingPool pool) {
        m_owningPool = pool;
    }
   
    /**
     * Release instance, returning it to the available list. This method must be called when processing is completed.
     */
    public void releaseInstance() {
        synchronized (m_owningPool) {
            m_owningPool.releaseInstance(this);
        }
    }

    /**
     * Process service request. This first unmarshalls and processes the request headers through any configured
     * handlers, then passes the unmarshalled body payload object to the appropriate service for actual request
     * processing. The object returned by the service (if any) is then serialized out as the response body payload,
     * along with any headers added by handlers during the outbound processing.
     *
     * @param iconn the connection that the request is to be read from
     * @param oconn the connection that the response is to written to
     */
    public final void processRequest(InConnection iconn, OutServerConnection oconn) {
        OutContext outCtx = null;
        try {
            getProcessor().receiveMessage(iconn);
            Object body = getProcessor().getCurrentMessageContext().getBody();

            try {
                Object response = invokeOperation(body);

                getProcessor().switchMessageContext();
                if (logger.isDebugEnabled()) {
                    logger.debug("Sending response " + response);
                }
                outCtx = (OutContext) getProcessor().getCurrentMessageContext();
                outCtx.setBody(response);
                getProcessor().sendMessage(oconn);
            } catch (NoSuchMethodException e) {
                oconn.sendNotFoundError();
            }
        } catch (Throwable e) {
            // check if it's too late to send a fault response
            if (oconn.isCommitted()) {
                logger.error("Aborted response due to error after commit", e);
            } else {
                getProcessor().switchMessageContext();
                outCtx = (OutContext) getProcessor().getCurrentMessageContext();
                getServiceExceptionHandler().handleException(e, getProcessor(), oconn);
            }
        } finally {
            try {
                if (m_outputCompletionListener != null && outCtx != null) {
                    logger.debug("Calling output completion listener");
                    m_outputCompletionListener.onComplete(new OutputCompletionEvent(outCtx));
                }
            } finally {
                getProcessor().reset();
            }
        }
    }

    private Object invokeOperation(Object payload) throws NoSuchMethodException, IllegalAccessException,
        InvocationTargetException, WsException {

        Operation op = getOperation(payload);
        if (op == null) {
            throw new NoSuchMethodException("No operation defined for payload type " + payload == null ? "null"
                : payload.getClass().getName());
        }

        return op.invoke(m_serviceObj, payload, getProcessor());
    }

    /**
     * Get operation for unmarshalled request body.
     *
     * @param body unmarshalled request data
     * @return corresponding operation information
     */
    private Operation getOperation(Object body) {
        String cname = "";
        if (body != null) {
            cname = body.getClass().getName();
        }
        if (logger.isDebugEnabled()) {
          logger.debug("Searching for operation mapped by class name '" + cname + "'");
      }
        return (Operation) m_operationByBodyMap.get(cname);
    }

    /**
     * Returns the binding factory for the binding that contains the definitions for all objects used by the outbound
     * body payload for this service.
     *
     * @return bindingFactory
     */
    public final IBindingFactory getOutBodyBindingFactory() {
        return m_outBodyBindingFactory;
    }

    /**
     * Returns the binding factory for the binding that contains the definitions for all objects used by the inbound
     * body payload for this service.
     *
     * @return bindingFactory
     */
    public final IBindingFactory getInBodyBindingFactory() {
        return m_inBodyBindingFactory;
    }

    /**
     * Returns the {@link Operation}s that this service offers.
     *
     * @return a collection of {@link Operation}
     */
    public final Collection getOperations() {
        return Collections.unmodifiableCollection(m_operationByBodyMap.values());
    }

    /**
     * Get serviceExceptionHandler.
     *
     * @return serviceExceptionHandler
     */
    protected ServiceExceptionHandler getServiceExceptionHandler() {
        return m_serviceExceptionHandler;
    }

    /**
     * Create body handlers and add them to the contexts.
     *
     * @param inCtx inbound message context
     * @param outCtx outbound message context
     * @throws WsBindingException on error creating handlers
     */
    protected void createBodyHandlers(InContext inCtx, OutContext outCtx) throws WsBindingException {
        IBindingFactory outBodyBindingFactory = getOutBodyBindingFactory();
        if (outBodyBindingFactory != null) {
            outCtx.setBodyWriter(new MarshallingPayloadWriter(outBodyBindingFactory));
        }
   
        IBindingFactory inBodyBindingFactory = getInBodyBindingFactory();
        if (inBodyBindingFactory != null) {
            inCtx.setBodyReader(new UnmarshallingPayloadReader(inBodyBindingFactory));
        }
    }
    /**
     * Sets the message contexts on all transport options associated with this service.
     *
     * @param inCtx inbound message context
     * @param outCtx outbound message context
     */
    protected void setContextOnTransportOptions(InContext inCtx, OutContext outCtx) {
        for (Iterator iterator = m_transportOptionsMap.values().iterator(); iterator.hasNext();) {
            TransportOptions options = (TransportOptions) iterator.next();
            options.setMessageContexts(inCtx, outCtx);
        }
    }
  
    /**
     * Get the processor for processing the messages.
     *
     * @return processor
     */
    protected Processor getProcessor() {
        return m_processor;
    }

   /**
    * Returns formatting options for outbound xml.
    *
    * @return xml formatting options
    */
   public XmlOptions getXmlOptions() {
       return m_xmlOptions;
   }
  
   /**
    * Returns the transport options of the specified type.
    *
    * @param optionsClass the type of transport options that are required
    * @return transport options of the specified type, or <code>null</code> if no options defined for this type of
    * transport
    */
   public TransportOptions getTransportOptions(Class optionsClass) {
       return (TransportOptions) m_transportOptionsMap.get(optionsClass.getName());
   }

    /**
     * Returns a mapper for mapping media codes to media types.
     *
     * @return media type mapper
     */
    public MediaTypeMapper getMediaTypeMapper() {
        return m_mediaTypeMapper;
    }

    /**
     * Sets a WSDL provider that will create or retrieve the WSDL for this service.
     *
     * @param wsdlProvider WSDL provider
     */
    public abstract void setWsdlProvider(WsdlProvider wsdlProvider);

    /**
     * Returns the WSDL provider for this service.
     *
     * @return WSDL provider  or null if no WSDL provider defined
     */
    public abstract WsdlProvider getWsdlProvider();
}
TOP

Related Classes of org.jibx.ws.server.Service

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.