Package org.apache.beehive.wsm.axis

Source Code of org.apache.beehive.wsm.axis.EnhancedBeanDeSerializerFactory

/*
* Copyright 2001-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.beehive.wsm.axis;

import org.apache.axis.constants.Style;
import org.apache.axis.constants.Use;
import org.apache.axis.description.*;
import org.apache.axis.encoding.*;
import org.apache.axis.encoding.ser.ArrayDeserializerFactory;
import org.apache.axis.encoding.ser.ArraySerializerFactory;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.apache.axis.utils.BeanPropertyDescriptor;
import org.apache.axis.utils.BeanUtils;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.wsdl.fromJava.Namespaces;
import org.apache.axis.wsdl.fromJava.Types;
import org.apache.beehive.wsm.axis.databinding.SystemTypeLookupService;
import org.apache.beehive.wsm.axis.registration.AxisTypeMappingMetaData;
import org.apache.beehive.wsm.axis.util.encoding.XmlBeanDeserializerFactory;
import org.apache.beehive.wsm.axis.util.encoding.XmlBeanSerializerFactory;
import org.apache.beehive.wsm.databinding.BindingLookupService;
import org.apache.beehive.wsm.model.BeehiveWsMethodMetadata;
import org.apache.beehive.wsm.model.BeehiveWsParameterMetadata;
import org.apache.beehive.wsm.model.BeehiveWsSOAPBindingInfo;
import org.apache.beehive.wsm.model.BeehiveWsTypeMetadata;
import org.apache.beehive.wsm.util.InvalidTypeMappingException;
import org.apache.beehive.wsm.registration.TypeRegistrar;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlObject;

import javax.jws.WebParam;
import javax.jws.soap.SOAPBinding;
import javax.wsdl.OperationType;
import javax.xml.namespace.QName;
import javax.xml.rpc.holders.Holder;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
* ****************************************************************************
*
* @author Jonathan Colwell
*/
public class AxisHook {
    static Logger logger = Logger.getLogger(AxisHook.class);

    public static ServiceDesc createServiceDesc(BeehiveWsTypeMetadata wsm,
            ClassLoader cl) throws ClassNotFoundException,
            NoSuchMethodException, InvalidTypeMappingException {
        JavaServiceDesc sd = new JavaServiceDesc();
        if (null == cl) {
            /*
             * NOTE jcolwell@bea.com 2004-Aug-30 -- if no classloader was
             * provided, use the one that loaded this Class
             */
            cl = AxisHook.class.getClassLoader();
        }
        final Class serviceClass = cl.loadClass(wsm.getClassName());

        // Create a list of the allowed methods
        List<String> allowedMethods = new ArrayList<String>();
        for (BeehiveWsMethodMetadata meth : wsm.getMethods()) {
            String method = meth.getJavaMethodName();
            allowedMethods.add(method);
        }

        // set the ServiceDesc base information
        sd.setName(wsm.getWsName());
        sd.setImplClass(serviceClass);
        String targetNamespace = wsm.getWsTargetNamespace();
        sd.setDefaultNamespace(targetNamespace);
        sd.setAllowedMethods(allowedMethods);
        configureSoapBinding(sd, wsm.getSoapBinding());

        TypeMappingRegistry tmr = new TypeMappingRegistryImpl(true);
        TypeMapping tm = tmr
                .getOrMakeTypeMapping(sd.getUse() == Use.ENCODED ? "http://schemas.xmlsoap.org/soap/encoding/" //"encoded"
                        : "");
        sd.setTypeMappingRegistry(tmr);
        sd.setTypeMapping(tm);

        /*
         * jongjinchoi@apache.org 2005-Mar-16 -- Use Axis's introspection
         * feature instead of creating new OperationDesc and ParameterDescs
         * directly. The introspected OperationDesc and ParameterDescs are
         * overrided by WSM. When appropriate type mapping registry is set, Axis
         * fills the ParameterDesc's typeEntry from the preset typemapping
         * registry, which is required for Axis to work in wrapped/lit mode.
         */
        sd.getOperations();

        // Walk the methods
        for (BeehiveWsMethodMetadata meth : wsm.getMethods()) {
            String operationName = meth.getWmOperationName();
            if (null != operationName && 0 < operationName.length()) {
                // set the Operations properties
                OperationDesc od = sd.getOperationByName(meth
                        .getJavaMethodName());
                od.setElementQName(new QName(targetNamespace, operationName));
                od.setName(operationName);
                od.setSoapAction(meth.getWmAction());
                if (meth.isOneWay()) {
                    od.setMep(OperationType.ONE_WAY);
                } else {
          String namespace = "" ;
          // namespace only should be added for document style, RPC style doesn't need name space for return type.
          if( wsm.getSoapBinding().getStyle() == SOAPBinding.Style.DOCUMENTnamespace = meth.getWrTargetNamespace();
                    od.setReturnQName(new QName(namespace,
                            meth.getWrName()));
                    final Class returnType = meth.getJavaReturnType();
                    QName qn = configureTypeMapping(sd, returnType, meth
                            .getWrTargetNamespace());
                    od.setReturnType(qn);
                    od.setReturnClass(returnType);
                }

                // process the parameters
                int pcnt = 0;
                for (BeehiveWsParameterMetadata param : meth.getParams()) {
                    ParameterDesc pd = od.getParameter(pcnt++);
                    final Class paramType = param.getJavaType();

                    if (pd.getTypeQName() == null) { // set the typeQName if
                        // it is not set
                        // already.
                        QName typeQName = configureTypeMapping(sd, paramType,
                                param.getWpTargetNamespace());
                        /*
                         * jongjinchoi@apache.org 2005-Mar-16 -- The typeQName
                         * from configureTypeMapping() is not dummy. This is
                         * required to find ArrayDeserializer when the
                         * document/literal bare array is deserialized.
                         */
                        pd.setTypeQName(typeQName);
                    }

                    // set QName
          String namespace = "" ;
          // namespace only should be added for document style, RPC style doesn't need name space for parameter names.
          if( wsm.getSoapBinding().getStyle() == SOAPBinding.Style.DOCUMENTnamespace = param.getWpTargetNamespace();
                    QName paramQName = new QName(namespace,
                            param.getWpName());
                    pd.setQName(paramQName);

                    // set Mode
                    final boolean header = param.isWpHeader();
                    final WebParam.Mode mode = param.getWpMode();
                    switch (mode) {
                    case IN:
                        pd.setMode(ParameterDesc.IN);
                        pd.setInHeader(header);
                        pd.setOutHeader(false);
                        break;
                    case OUT:
                        pd.setMode(ParameterDesc.OUT);
                        pd.setInHeader(false);
                        pd.setOutHeader(header);
                        break;
                    case INOUT:
                        pd.setMode(ParameterDesc.INOUT);
                        pd.setInHeader(header);
                        pd.setOutHeader(header);
                        break;
                    default:
                        throw new IllegalArgumentException(
                                "Illegal value for WebParam.Mode: " + mode);
                    }

                    // set JavaType
                    pd.setJavaType(paramType);
                }

                // set Exceptions
                Method javaMethod = od.getMethod();
                for (Class thrown : javaMethod.getExceptionTypes()) {
                    FaultDesc fd = od.getFaultByClass(thrown);
                    if (null == fd) {
                        logger
                                .info("Exception: "
                                        + thrown.getCanonicalName()
                                        + " is not picked up by the Axis, only non Remote and Application Specific exceptions are registed in Axis.  This is not a fatal error.");
                        continue;
                    }
                    QName qname = configureTypeMapping(sd, thrown, meth
                            .getWrTargetNamespace());
                    fd.setXmlType(qname);
                    fd.setQName(qname);
                    fd.setComplex(true);
                }
            }
        }
        return sd;
    }

    /**
     * This method will return a boolean value indicating that Activation is
     * enabled. Activation requires the DataHandler and the Multipart Classes to
     * both be found.
     *
     * @return boolean indicating that Activation is enabled.
     */
    private static boolean isActivationEnabled() {
        return null != getDataHandlerClass() && null != getMultipartClass();
    }

    /**
     * This will return the Class for the DataHandler. This will return null if
     * the DataHandler is not available.
     *
     * @return The DataHandler Class or null if the DataHandler is not found
     */
    private static Class getDataHandlerClass() {
        try {
            return AxisHook.class.getClassLoader().loadClass(
                    "javax.activation.DataHandler");
        } catch (ClassNotFoundException e) {
            // ignore the class was not found
        }
        return null;
    }

    /**
     * This will return the Class for the MimeMultipart handler. It will return
     * null if the MimMultipart class is not available.
     *
     * @return The MimeMultipart Class or null if the DataHandler is not found.
     */
    private static Class getMultipartClass() {
        try {
            return AxisHook.class.getClassLoader().loadClass(
                    "javax.mail.internet.MimeMultipart");
        } catch (ClassNotFoundException e) {
            // ignore the class was not found
        }
        return null;
    }

    private static QName configureTypeMapping(ServiceDesc desc, Class type,
            String defaultNameSpace) throws InvalidTypeMappingException {
        try {
            if (Void.TYPE.equals(type))
                return null;
           
//            // If type is holder or it is generic holder, strip it to its base type
//            type = TypeRegistrar.getUnderlyingType(type);
//           
            if (AxisTypeMappingMetaData.isBuiltInType(type))
                return AxisTypeMappingMetaData.getBuiltInTypeQname(type);
           
            if(Holder.class.isAssignableFrom(type )) {
                type = TypeRegistrar.getHoldersValueClass(type);
                // NOTE: May need to register the holder type also.              
             }

            // if type needs to be registered
            TypeMapping tm = desc.getTypeMapping();
            // QName q = tm.getTypeQName(type);
            // System.out.println("###################################type: "
            // + type.getCanonicalName() + " qname is: " + q
            // + " default namesapce: " + defaultNameSpace);
            // if (null == q || // Not registered
            // !q.getNamespaceURI().equals(defaultNameSpace)) { // registered
            // // but not
            // // in
            // // current
            // // namespace
            // // q = generateQName(type, desc);
            // q = new QName(defaultNameSpace, Types
            // .getLocalNameFromFullName(type.getName()));
            // System.out
            // .println("CREATE QNAME: #############################type: "
            // + type.getCanonicalName() + " qname is: " + q);
            // }

            BindingLookupService lookupService = new SystemTypeLookupService()// move this to the constructor
            QName q = lookupService.class2qname(type, defaultNameSpace);

            if (type.isArray()) {
                /*
                 * jongjinchoi@apache.org 2005-Mar-16 -- don't register array
                 * serializer in document(bare or wrapped)/literal mode.
                 */
                if (!tm.isRegistered(type, q) && desc.getStyle() == Style.RPC
                        && desc.getUse() == Use.ENCODED) {
                    tm.register(type, q, new ArraySerializerFactory(type, q),
                            new ArrayDeserializerFactory());
                }
                QName qcomp = configureTypeMapping(desc, type
                        .getComponentType(), defaultNameSpace);
                if (desc.getUse() == Use.LITERAL) {
                    q = qcomp;
                }
            } else if (!tm.isRegistered(type, q)) {
                 if (XmlObject.class.isAssignableFrom(type)) {
                    q = XmlBeans.typeForClass(type).getName();
                    tm.register(type, q, new XmlBeanSerializerFactory(type, q),
                            new XmlBeanDeserializerFactory(type, q));
                }
                /*
                 * NOTE jcolwell@bea.com 2004-Oct-11 -- these datahandler using
                 * classes are generally already registered but just in case...
                 */
                else if (isActivationEnabled()
                        && (java.awt.Image.class.isAssignableFrom(type)
                                || getMultipartClass().isAssignableFrom(type) || getDataHandlerClass()
                                .isAssignableFrom(type))) {
                    try {
                        /*
                         * NOTE jcolwell@bea.com 2004-Oct-08 -- doing reflection
                         * here in case AXIS was built without attachment
                         * support.
                         */
                        ClassLoader cl = AxisHook.class.getClassLoader();
                        // Loadclass could have been done in import also, but if
                        // there are no activation.jar then this would
                        // cause error when the class is loaded. To prevent that
                        // we load the class explicitly at this point.
                        // if we had done the "new
                        // org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory"
                        // then the class
                        // would have had dependecies to the org.apache... class
                        // which would not have worked in case activation was
                        // not on the path.
                        Class<SerializerFactory> sfClass = (Class<SerializerFactory>) cl
                                .loadClass("org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory");
                        Class<DeserializerFactory> dsfClass = (Class<DeserializerFactory>) cl
                                .loadClass("org.apache.axis.encoding.ser.JAFDataHandlerDeserializerFactory");
                        Constructor<SerializerFactory> sfCon = sfClass
                                .getConstructor(Class.class, QName.class);
                        Constructor<DeserializerFactory> dsfCon = dsfClass
                                .getConstructor(Class.class, QName.class);
                        SerializerFactory sf = sfCon.newInstance(type, q);
                        DeserializerFactory dsf = dsfCon.newInstance(type, q);
                        tm.register(type, q, sf, dsf);
                    } catch (Exception e) {
                        /*
                         * FIXME jcolwell@bea.com 2004-Oct-08 -- log this
                         * properly
                         */
                        e.printStackTrace();
                    }
                } else if (!Remote.class.isAssignableFrom(type)
                /*
                 * NOTE jcolwell@bea.com 2004-Dec-01 -- java.rmi.Remote is
                 * prohibited by the jax-rpc spec
                 *
                 * NOTE jcolwell@bea.com 2004-Oct-11 -- restricting against
                 * File, since it doesn't make sense to serialize as a bean. It
                 * causes an infinite loop as it keeps returning itself from the
                 * getAbsoluteFile and getCanonicalFile calls
                 */
                && !File.class.isAssignableFrom(type)) {
                    TypeDesc td = TypeDesc.getTypeDescForClass(type);

                    // if type was registered in a different namespace, then
                    // ignore this and create a new td
                    if (td != null
                            && !td.getXmlType().getNamespaceURI().equals(
                                    q.getNamespaceURI())) {
                        td = null;
                    }
                    TypeDesc superTd = null;
                    BeanPropertyDescriptor[] superPd = null;
                    // type desc is used for java-xml mapping, make sure the
                    // class and all its super classes have a type desc defined.
                    if (null == td) {
                        td = new TypeDesc(type); // create type descriptor
                        // for this class --- NOT
                        // its super classes at this
                        // point.
                        // add super class types.
                        Class supa = type.getSuperclass();
                        if ((supa != null) && (supa != java.lang.Object.class)
                                && (supa != java.lang.Exception.class)
                                && (supa != java.lang.Throwable.class)
                                && (supa != java.rmi.RemoteException.class)
                                && (supa != org.apache.axis.AxisFault.class)) {
                            configureTypeMapping(desc, supa, defaultNameSpace);
                        }
                        // check to see if a type mapping was created for the
                        // super class.
                        superTd = TypeDesc.getTypeDescForClass(supa);
                        if (superTd != null) // super class is a regular java
                        // bean with axis typedesc.
                        {
                            superPd = superTd.getPropertyDescriptors(); // this
                            // is
                            // mapping
                            // for
                            // all
                            // my
                            // super
                            // classes.
                        }
                        td.setXmlType(q);
                        TypeDesc.registerTypeDescForClass(type, td);
                        // NOTE: this is partially finished td, so more
                        // processing to follow that is why its td is not set to
                        // null as it is
                        // for the case when it is already provided (when td
                        // !=null)
                    } else {
                        td = null; // we don't need type desc. any more this is
                        // a complete td
                    }
                    //
                    // // At this all parent bean classes and their properties
                    // // (attributes) have been registered with typedecriptor
                    // and
                    // // type mapping.
                    // // next regidster type for this class.
                    // tm.register(type, q, new BeanSerializerFactory(type, q),
                    // /*
                    // * NOTE jcolwell@bea.com 2004-Oct-11 -- should check that
                    // * the type to deserialize has a default contructor but
                    // with
                    // * this setup there is no way to know if it is used only
                    // in
                    // * serialization.
                    // */
                    // new BeanDeserializerFactory(type, q));

                    // At this all parent bean classes and their properties
                    // (attributes) have been registered with typedecriptor and
                    // type mapping.
                    // next regidster type for this class.
                    tm.register(type, q, new EnhancedBeanSerializerFactory(
                            type, q, td),
                    /*
                     * NOTE jcolwell@bea.com 2004-Oct-11 -- should check that
                     * the type to deserialize has a default contructor but with
                     * this setup there is no way to know if it is used only in
                     * serialization.
                     */
                    new EnhancedBeanDeSerializerFactory(type, q, td));

                    // now register the types for this bean properties
                    // (attributes)
                    // Note: we have to consider the case that one of the
                    // properties may be XML bean
                    // or a class that can deal with its own serialization.
                    Map serProps = BeanDeserializerFactory.getProperties(type,
                            null); // Note this is all of the bean properties
                    for (BeanPropertyDescriptor beanProps : (Collection<BeanPropertyDescriptor>) serProps
                            .values()) {
                        Class subType = beanProps.getType();
                        // make sure the property type is configred with Type
                        // mapping and its serializer information
                        if (!(subType.isPrimitive()
                                || subType.getName().startsWith("java.") || subType
                                .getName().startsWith("javax."))) {
                            configureTypeMapping(desc, subType,
                                    defaultNameSpace); // if this was XML bean
                            // this recursion would
                            // take care of it
                        }

                        if (td != null) { // if I didn't have type descriptor
                            // when I came to this method... I
                            // created partially filled one
                            // above
                            // now need to complete this.
                            String ns = q.getNamespaceURI(); // name space
                            // for the class
                            // if there is
                            // no parent
                            // find proper namespace for this element... we need
                            // to find out whihc class in the hierarchy the
                            // element came from
                            // once you know where the element came form (which
                            // class) then you know the element's name space.
                            if (superTd != null && superPd != null) { // if I
                                // had a
                                // parent,
                                for (int j = 0; j < superPd.length; j++) {
                                    if (beanProps.getName().equals(
                                            superPd[j].getName())) {
                                        ns = superTd.getXmlType()
                                                .getNamespaceURI();
                                        break;
                                    }
                                }
                            }
                            FieldDesc fd = new ElementDesc();
                            fd.setJavaType(subType);
                            fd.setFieldName(beanProps.getName());
                            fd.setXmlName(new QName(ns, beanProps.getName()));
                            // NOTE jcolwell@bea.com 2004-Oct-28 -- might need
                            // to do more to ensure a useful type QName.
                            fd.setXmlType(tm.getTypeQName(subType));
                            ((ElementDesc) fd).setNillable(true);
                            // mmerz@apache.com 2005-Mar-09: required since Axis
                            // 1.2RC3 to allow for null values in complex
                            // objects; note that there is no (JSR-181)
                            // annotation that allows configuring this value.
                            td.addFieldDesc(fd);
                        }
                    }
                } else {
                    throw new InvalidTypeMappingException("failed to register "
                            + type.getName()
                            + " as a valid web service datatype,"
                            + " consider using a custom type mapping");
                }
            }
            return q;

        } catch (RuntimeException e) {
            logger.error("Error in type registeration", e);
            e.printStackTrace();
            throw e;
        }
    }

    private static QName generateQName(Class type, ServiceDesc desc) {
        String namespace = Namespaces.makeNamespace(type.getName());
        if (namespace == null || namespace.endsWith("DefaultNamespace")) {
            namespace = desc.getDefaultNamespace();
        }
        return new QName(namespace, Types.getLocalNameFromFullName(type
                .getName()));
    }

    protected static void configureSoapBinding(ServiceDesc sd,
            BeehiveWsSOAPBindingInfo sbi) {
        javax.jws.soap.SOAPBinding.Style style = javax.jws.soap.SOAPBinding.Style.DOCUMENT;
        javax.jws.soap.SOAPBinding.Use use = javax.jws.soap.SOAPBinding.Use.LITERAL;
        javax.jws.soap.SOAPBinding.ParameterStyle paramStyle = javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED;
        if (sbi != null) {
            style = sbi.getStyle();
            use = sbi.getUse();
            paramStyle = sbi.getParameterStyle();
        }
        if (style == javax.jws.soap.SOAPBinding.Style.RPC) {
            sd.setStyle(Style.RPC);
            if (use == javax.jws.soap.SOAPBinding.Use.ENCODED) {
                sd.setUse(Use.ENCODED);
            } else {
                sd.setUse(Use.LITERAL);
            }
        } else {
            /*
             * since DOCUMENT ENCODED is not valid so force to use LITERAL
             */
            sd.setUse(Use.LITERAL);

            // check if this is a wrapped document literal
            if (paramStyle == javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED) {
                sd.setStyle(Style.WRAPPED);
            } else {
                // just regular document style
                sd.setStyle(Style.DOCUMENT);
            }
        }
    }
}

/*
* A TEMP SOLUTION TO BEAN Serialization/Deserialization problem The problem is
* that the Axis factories use the types that are defined for a class, so a
* given class can't be in multiple name spaces. In this solution the factory
* gets the type descriptor in the constructor
*/

class EnhancedBeanSerializerFactory extends BeanSerializerFactory {
    public EnhancedBeanSerializerFactory(Class javaType, QName xmlType,
            TypeDesc typeDesc) {
        super(javaType, xmlType);

        this.typeDesc = typeDesc;

        if (typeDesc != null) {
            propertyDescriptor = typeDesc.getPropertyDescriptors();
        } else {
            propertyDescriptor = BeanUtils.getPd(javaType, null);
        }
    }
}

class EnhancedBeanDeSerializerFactory extends BeanDeserializerFactory {
    public EnhancedBeanDeSerializerFactory(Class javaType, QName xmlType,
            TypeDesc typeDesc) {
        super(javaType, xmlType);

        this.typeDesc = typeDesc;
        propertyMap = getProperties(javaType, typeDesc);
    }

}
TOP

Related Classes of org.apache.beehive.wsm.axis.EnhancedBeanDeSerializerFactory

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.