WebServiceWrapper.java :  » Net » Coadunation_1.0.1 » com » rift » coad » lib » webservice » Java Open Source

Java Open Source » Net » Coadunation_1.0.1 
Coadunation_1.0.1 » com » rift » coad » lib » webservice » WebServiceWrapper.java
/*
 * CoadunationLib: The coaduntion implementation library.
 * Copyright (C) 2006  Rift IT Contracting
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * WebServiceWrapper.java
 *
 * The web service wrapper responsible for wrapping the SOAP web service
 * information.
 */

// package paths
package com.rift.coad.lib.webservice;

// java imports
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.ClassLoader;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Vector;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import java.lang.reflect.Method;
import javax.naming.InitialContext;
import javax.transaction.Status;
import javax.transaction.UserTransaction;


// w3c imports
import org.w3c.dom.Document;

// logging import
import org.apache.log4j.Logger;

// ibm imports
import com.ibm.wsdl.OperationImpl;

// jaxb imports
import javax.xml.soap.MimeHeaders;

// axis imports
import org.apache.axis.AxisFault;
import org.apache.axis.AxisEngine;
import org.apache.axis.Constants;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.constants.Scope;
import org.apache.axis.description.ServiceDesc;
import org.apache.axis.handlers.soap.SOAPService;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPFault;
import org.apache.axis.providers.java.RPCProvider;
import org.apache.axis.transport.http.NonBlockingBufferedInputStream;
import org.apache.axis.server.AxisServer;
import org.apache.axis.utils.ClassUtils;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.XMLUtils;
import org.apache.axis.wsdl.gen.Parser;
import org.apache.axis.wsdl.symbolTable.SymbolTable;
import org.apache.axis.wsdl.symbolTable.BaseType;
import org.apache.axis.wsdl.symbolTable.BindingEntry;
import org.apache.axis.wsdl.symbolTable.Type;
import org.apache.axis.wsdl.symbolTable.DefinedType;
import javax.xml.rpc.encoding.TypeMapping;
import javax.xml.rpc.encoding.TypeMappingRegistry;


// coadunation imports
import com.rift.coad.lib.configuration.Configuration;
import com.rift.coad.lib.configuration.ConfigurationFactory;
import com.rift.coad.lib.deployment.WebServiceInfo;
import com.rift.coad.lib.deployment.DeploymentLoader;
import com.rift.coad.lib.httpd.HttpDaemon;
import com.rift.coad.lib.httpd.MimeTypes;
import com.rift.coad.lib.thirdparty.axis.AxisManager;
import com.rift.coad.lib.thirdparty.axis.AxisException;

/**
 * The web service wrapper responsible for wrapping the SOAP web service
 * information.
 *
 * @author Brett Chaldecott
 */
public class WebServiceWrapper {
    
    // the class log variable
    protected Logger log =
            Logger.getLogger(WebServiceWrapper.class.getName());
    
    // class constants
    private final static String PARSER_PATTERN =
            "address[\\s]+location=\"[a-z0-9://_-]*\"";
    private final static String SERVICE_END_POINT_FORMAT =
            "address location=\"http://%s:%d%s\"";
    private final static String URL_FORMAT =
            "http://%s:%d%s";
    private final static String HOST = "host";
    private final static String PORT = "port";
    private final static String TRANSPORT_NAME = "SimpleHTTP";
    
    
    // the classes member variables
    private String serviceEndPoint = null;
    private String url = null;
    private String path = null;
    private String role = null;
    private SOAPService service = null;
    private ClassLoader classLoader = null;
    private boolean transaction = false;
    private InitialContext context = null;
    private UserTransaction ut = null;
    private Vector extraClasses = null;
    
    /**
     * Creates a new instance of WebServiceWrapper.
     *
     * @param webServiceInfo The reference to the web service information.
     * @param deploymentLoader The object responsible for loading a deployment
     *        file.
     * @exception WebServiceException
     */
    public WebServiceWrapper(WebServiceInfo webServiceInfo,
            DeploymentLoader deploymentLoader)
            throws WebServiceException {
        
        try {
            Configuration config = ConfigurationFactory.getInstance().
                    getConfig(WebServiceWrapper.class);
            context = new InitialContext();
            
            String host = config.getString(HOST,
                    InetAddress.getLocalHost().getCanonicalHostName());
            int port = (int)config.getLong(PORT,HttpDaemon.DEFAULT_PORT);
            
            path = webServiceInfo.getPath();
            role = webServiceInfo.getRole();
            serviceEndPoint = String.format(
                    SERVICE_END_POINT_FORMAT,host,port,path);
            url = String.format(URL_FORMAT,host,port,path);
            
            // retrieve a reverence to the axis engine
            AxisEngine engine = AxisManager.getInstance().getServer();
            
            // test the class
            deploymentLoader.getClass(webServiceInfo.getClassName());
            
            // try different handlers
            service = new SOAPService(new WebServiceInvoker(
                    deploymentLoader.getClassLoader()));
            
            service.setName(webServiceInfo.getClassName());
            service.setOption(RPCProvider.OPTION_CLASSNAME,
                    webServiceInfo.getClassName());
            service.setOption(RPCProvider.OPTION_ALLOWEDMETHODS, "*");
            service.setOption(RPCProvider.OPTION_SCOPE,
                    Scope.DEFAULT.getName());
            
            // validate
            String wsdlPath = deploymentLoader.getTmpDir().getAbsolutePath() +
                    File.separator +
                    webServiceInfo.getWSDLPath();
            validate(wsdlPath,deploymentLoader.getClassLoader().loadClass(
                    webServiceInfo.getClassName()));
            extraClasses = webServiceInfo.getClasses();
            generateTypeMapping(wsdlPath,deploymentLoader.getClassLoader().loadClass(
                    webServiceInfo.getClassName()));
            // wsdl parameters
            ServiceDesc serviceDesc = service.getServiceDescription();
            serviceDesc.setWSDLFile(wsdlPath);
            
            service.setEngine(engine);
            service.init();
            
            // set the class loader
            classLoader = deploymentLoader.getClassLoader();
            
            // set the class loader
            ClassUtils.setClassLoader(webServiceInfo.getClassName(),
                    deploymentLoader.getClassLoader());
            
            transaction = webServiceInfo.getTransaction();
            if (transaction) {
                ut = (UserTransaction)context.lookup(
                        "java:comp/UserTransaction");
            }
            
            
            
        } catch (Exception ex) {
            throw new WebServiceException(
                    "Failed to initialize the web service : " + ex.getMessage()
                    ,ex);
        }
    }
    
    
    /**
     * This method returns the path to the web service.
     *
     * @return The string containing the path to the web service.
     */
    public String getPath() {
        return path;
    }
    
    
    /**
     * This method returns the role of the web service.
     *
     * @return The string containing the role information.
     */
    public String getRole() {
        return role;
    }
    
    
    /**
     * This method return the reference to the soap service.
     *
     * @return The reference to the soap service.
     */
    public SOAPService getService() {
        return service;
    }
    
    
    /**
     * This method returns the class loader that can be used to load the service.
     *
     * @return The reference to the class loader.
     */
    public ClassLoader getClassLoader() {
        return classLoader;
    }
    
    
    /**
     * This method returns a string containing the genreated WSDL
     *
     * @return String The string containing the wsdl.
     * @exception WebServiceException
     */
    public String generateWSDL() throws
            WebServiceException {
        // setup the axis engine
        MessageContext msgContext = null;
        try {
            msgContext = setupContext();
            AxisServer engine = AxisManager.getInstance().getServer();
            engine.generateWSDL(msgContext);
            Document doc = (Document) msgContext.getProperty("WSDL");
            XMLUtils.normalize(doc.getDocumentElement());
            String wsdl = XMLUtils.PrettyDocumentToString(doc);
            Pattern pattern = Pattern.compile(PARSER_PATTERN,
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
            return pattern.matcher(wsdl).replaceFirst(serviceEndPoint);
        } catch (WebServiceException ex) {
            throw ex;
        } catch (AxisException ex) {
            throw new WebServiceException(
                    "Failed to retrieve a valid axis reference : " +
                    ex.getMessage(),ex);
        } catch (Exception ex) {
            processException(msgContext,ex);
        }
        // this statement will never get reached as the process exception will
        // always throw, but to prevent the compiler from complaining it is
        // put here.
        return null;
    }
    
    
    /**
     * This message takes a string message block and returns a string message
     * block.
     *
     * @return The string result.
     * @param message The message to process.
     * @exception WebServiceException
     */
    public String processRequest(String message) throws WebServiceException {
        return processRequest(new ByteArrayInputStream(message.getBytes()),
                new MimeHeaders());
    }
    
    
    /**
     * This method process the input stream passed to it and returns a string
     * message.
     *
     * @return An XML string result of the request.
     * @param in The input stream containing the message to process.
     * @param headers The headers to process.
     * @exception WebServiceException
     */
    public String processRequest(InputStream in, MimeHeaders headers) throws
            WebServiceException {
        MessageContext msgContext = null;
        boolean ownTransaction = false;
        try {
            if (transaction &&
                    (ut.getStatus() == Status.STATUS_NO_TRANSACTION)) {
                ut.begin();
                ownTransaction = true;
            }
            msgContext = setupContext();
            AxisServer engine = AxisManager.getInstance().getServer();
            NonBlockingBufferedInputStream inputStream = new
                    NonBlockingBufferedInputStream();
            inputStream.setInputStream(in);
            
            // setup a message request
            Message requestMsg = new Message(inputStream,false,headers);
            msgContext.setRequestMessage(requestMsg);
            
            // invoke the request
            engine.invoke(msgContext);
            
            // retrieve the result
            Message responseMsg = msgContext.getResponseMessage();
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            responseMsg.writeTo(output);
            output.flush();
            if (ownTransaction) {
                ut.commit();
                ownTransaction = false;
            }
            return output.toString();
        } catch (WebServiceException ex) {
            throw ex;
        } catch (Exception ex) {
            processException(msgContext,ex);
        } finally {
            if (ownTransaction) {
                try {
                    if (ut.getStatus() == Status.STATUS_ACTIVE) {
                        ut.rollback();
                    }
                } catch (Exception ex2) {
                    log.error("Failed to rollback the changes : " +
                            ex2.getMessage(),ex2);
                }
            }
        }
        
        // this statement will never get reached as the process exception will
        // always throw, but to prevent the compiler from complaining it is
        // put here.
        return null;
    }
    
    
    /**
     * This method validates the WSDL file
     *
     * @param wsdlPath The path to the wsdl file to perform the validation for.
     * @param ref The reference to the class to validate.
     * @exception WebServiceException
     */
    private void validate(String wsdlPath,Class ref) throws WebServiceException {
        try {
            // TODO: Improve WSDL validation to check more than just the operation names
            // This will be done at a later date when it becomes a problem
            
            Parser parser = new Parser();
            parser.run(wsdlPath);
            Set operations = getOpertations(parser);
            Method[] methods = ref.getDeclaredMethods();
            for (Iterator iter = operations.iterator(); iter.hasNext();) {
                OperationImpl operation = (OperationImpl)iter.next();
                boolean found = false;
                for (int index = 0; index < methods.length; index++) {
                    if (methods[index].getName().equals(operation.getName())) {
                        found = true;
                    }
                }
                if (!found) {
                    throw new WebServiceException("The operation [" +
                            operation.getName() + "] not found on [" +
                            ref.getName() + "]");
                }
            }
        } catch (WebServiceException ex) {
            throw ex;
        } catch (Exception ex) {
            log.error("Validation on [" + wsdlPath + "] failed : " +
                    ex.getMessage(),ex);
            throw new WebServiceException("Validation on [" + wsdlPath
                    + "] failed : " + ex.getMessage(),ex);
        }
    }
    
    
    /**
     * This method returns the list of operations on a wsdl interface.
     *
     * @return The set containing the list of operations.
     * @param parser The reference to the parser
     */
    private Set getOpertations(Parser parser) throws WebServiceException {
        SymbolTable symbolTable = parser.getSymbolTable();
        Map index = symbolTable.getHashMap();
        for (Iterator iter = index.keySet().iterator(); iter.hasNext();) {
            QName key = (QName)iter.next();
            Object value = index.get(key);
            if (value instanceof Vector) {
                Vector list = (Vector)value;
                for (int count = 0; count < list.size(); count++) {
                    Object listValue = list.get(count);
                    if (listValue instanceof BindingEntry) {
                        BindingEntry entry = (BindingEntry)listValue;
                        return entry.getOperations();
                    }
                }
            }
        }
        throw new WebServiceException(
                "There are no operations defined for this wsdl file");
    }
    
    /**
     * This method validates the WSDL file
     *
     * @param wsdlPath The path to the wsdl file to perform the validation for.
     * @param ref The reference to the class to validate.
     * @exception WebServiceException
     */
    private void generateTypeMapping(String wsdlPath,Class ref) throws
            WebServiceException {
        try {
            Parser parser = new Parser();
            parser.run(wsdlPath);
            SymbolTable symbolTable = parser.getSymbolTable();
            Map index = parser.getSymbolTable().getTypeIndex();
            //this.service.getTypeMappingRegistry().register("http://schemas.xmlsoap.org/soap/encoding/")
            Map classTypes = getTypes(ref);
            TypeMappingRegistry registry = this.service.getTypeMappingRegistry();
            TypeMapping typeMapping = registry.createTypeMapping();
            for (Iterator iter = index.keySet().iterator(); iter.hasNext();) {
                QName key = (QName)iter.next();
                Type value = (Type)index.get(key);
                if (value instanceof DefinedType) {
                    DefinedType defType = (DefinedType)value;
                    
                    if (defType.getRefType() == null) {
                        Class refType = (Class)classTypes.get(defType.getQName().getLocalPart());
                        if (refType == null && 
                                defType.getQName().getLocalPart().equalsIgnoreCase("MapItem")) {
                            refType = java.util.Map.class;
                        } else if (refType == null) {
                            continue;
                        }
                        log.debug("Key [" + key.toString() + "] value [" +
                                defType.getQName().getLocalPart()
                                + "] [" + defType.getBaseType() + "] [" +
                                defType.toString() + "] [" + refType.getName() + "]");
                        typeMapping.register(refType,defType.getQName(),
                                new org.apache.axis.encoding.ser.BeanSerializerFactory(
                                refType,defType.getQName()),
                                new org.apache.axis.encoding.ser.BeanDeserializerFactory(
                                refType,defType.getQName()));
                    }
                    
                }
            }
            this.service.getTypeMappingRegistry().register(
                    "http://schemas.xmlsoap.org/soap/encoding/",typeMapping);
        } catch (Exception ex) {
            log.error("Failed to retrieve the type mapping [" + wsdlPath
                    + "] because : " +
                    ex.getMessage(),ex);
            throw new WebServiceException(
                    "Failed to retrieve the type mapping [" + wsdlPath
                    + "] failed : " + ex.getMessage(),ex);
        }
    }
    
    
    /**
     * This method setups the message context
     *
     * @returns A message context.
     * @exception WebServiceException
     */
    private MessageContext setupContext() throws WebServiceException {
        try {
            // setup the axis engine
            AxisServer engine = AxisManager.getInstance().getServer();
            MessageContext msgContext = new MessageContext(engine);
            msgContext.setTransportName(TRANSPORT_NAME);
            msgContext.setProperty(MessageContext.TRANS_URL, url);
            msgContext.setProperty(Constants.MC_REALPATH,path);
            msgContext.setProperty(Constants.MC_RELATIVE_PATH,path);
            msgContext.setService(service);
            return msgContext;
        } catch (Exception ex) {
            log.error("Failed to setup the message context : " +
                    ex.getMessage(),ex);
            throw new WebServiceException(
                    "Failed to setup the message context : " + ex.getMessage(),
                    ex);
        }
    }
    
    
    /**
     * This method handles the exceptions generated by axis generically.
     *
     * @param msgContext The message context.
     * @param ex The exception to throw
     */
    private void processException(MessageContext msgContext,Exception ex) throws
            WebServiceException {
        try {
            // check that the message context has been set
            if (msgContext == null) {
                throw new WebServiceException("Failed to process request : " +
                        ex.getMessage(),ex);
            }
            
            log.debug("Fault messsage is : " + ex.getMessage(), ex);
            
            // handle the axis fault
            AxisFault af;
            if (ex instanceof AxisFault) {
                af = (AxisFault) ex;
                log.debug(Messages.getMessage("serverFault00"), af);
            } else {
                af = AxisFault.makeFault(ex);
            }
            
            // There may be headers we want to preserve in the
            // response message - so if it's there, just add the
            // FaultElement to it.  Otherwise, make a new one.
            Message responseMsg = msgContext.getResponseMessage();
            if (responseMsg == null) {
                responseMsg = new Message(af);
                responseMsg.setMessageContext(msgContext);
            } else {
                SOAPEnvelope env = responseMsg.getSOAPEnvelope();
                env.clearBody();
                env.addBodyElement(new SOAPFault((AxisFault) ex));
            }
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            responseMsg.writeTo(output);
            output.flush();
            
            // throw an exception with the xml content wrapped properly
            throw new WebServiceException(output.toString(),MimeTypes.XML);
        } catch (WebServiceException ex2) {
            throw ex2;
        } catch (Exception ex2) {
            log.error("Failed to process the exception : " +
                    ex2.getMessage(),ex2);
            throw new WebServiceException(
                    "Failed to process the exception : " + ex2.getMessage(),
                    ex2);
        }
    }
    
    
    /**
     * This method returns the list of types for the class.
     */
    public Map getTypes(Class ref) throws WebServiceException {
        Map entries = new HashMap();
        Method[] methods = ref.getMethods();
        for (int index = 0; index < methods.length; index++) {
            Class returnType = methods[index].getReturnType();
            Class[] parameterTypes = methods[index].getParameterTypes();
            Class[] exceptionTypes = methods[index].getExceptionTypes();
            if (!returnType.isPrimitive() && !returnType.isArray()) {
                entries.put(returnType.getSimpleName(),returnType);
            } else if (returnType.isArray() &&
                    !returnType.getComponentType().isPrimitive()) {
                entries.put(returnType.getComponentType().getSimpleName(),
                        returnType.getComponentType());
            }
            for (int paramIndex = 0; paramIndex < parameterTypes.length;
            paramIndex++) {
                Class param = parameterTypes[paramIndex];
                if (!param.isPrimitive() && !param.isArray()) {
                    entries.put(param.getSimpleName(),param);
                } else if (param.isArray() &&
                        !param.getComponentType().isPrimitive()) {
                    entries.put(param.getComponentType().getSimpleName(),
                            param.getComponentType());
                }
            }
            for (int exceptionIndex = 0; exceptionIndex < exceptionTypes.length;
            exceptionIndex++) {
                Class exception = exceptionTypes[exceptionIndex];
                if (exception != java.rmi.RemoteException.class) {
                    entries.put(exception.getSimpleName(),exception);
                }
            }
            
        }
        for (int index = 0; index < extraClasses.size(); index++) {
            String className = (String)extraClasses.get(index);
            try {
                Class extraClass = Class.forName(
                        className,false,ref.getClassLoader());
                entries.put(extraClass.getSimpleName(),extraClass);
            } catch (Throwable ex) {
                log.error("Failed to retrieve the class [" + className 
                        + "] because : " + ex.getMessage(),ex);
            }
        }
        return entries;
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.