Package org.apache.cxf.jaxb

Source Code of org.apache.cxf.jaxb.JAXBContextInitializer

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cxf.jaxb;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;

import org.apache.cxf.common.classloader.JAXBClassLoaderUtils;
import org.apache.cxf.common.jaxb.JAXBUtils;
import org.apache.cxf.common.util.ASMHelper;
import org.apache.cxf.common.util.ASMHelper.ClassWriter;
import org.apache.cxf.common.util.ASMHelper.MethodVisitor;
import org.apache.cxf.common.util.ASMHelper.Opcodes;
import org.apache.cxf.common.util.ReflectionUtil;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.service.ServiceModelVisitor;
import org.apache.cxf.service.model.MessageInfo;
import org.apache.cxf.service.model.MessagePartInfo;
import org.apache.cxf.service.model.OperationInfo;
import org.apache.cxf.service.model.ServiceInfo;
import org.apache.cxf.service.model.UnwrappedOperationInfo;

/**
* Walks the service model and sets up the classes for the context.
*/
class JAXBContextInitializer extends ServiceModelVisitor {

    private Set<Class<?>> classes;
    private Collection<Object> typeReferences;
    private Set<Class<?>> globalAdapters = new HashSet<Class<?>>();
    private Map<String, Object> unmarshallerProperties;

    public JAXBContextInitializer(ServiceInfo serviceInfo,
                                  Set<Class<?>> classes,
                                  Collection<Object> typeReferences,
                                  Map<String, Object> unmarshallerProperties) {
        super(serviceInfo);
        this.classes = classes;
        this.typeReferences = typeReferences;
        this.unmarshallerProperties = unmarshallerProperties;
    }

    @Override
    public void begin(MessagePartInfo part) {
        Class<?> clazz = part.getTypeClass();
        if (clazz == null) {
            return;
        }

        if (Exception.class.isAssignableFrom(clazz)) {
            //exceptions are handled special, make sure we mark it
            part.setProperty(JAXBDataBinding.class.getName() + ".CUSTOM_EXCEPTION",
                             Boolean.TRUE);
        }
        boolean isFromWrapper = part.getMessageInfo().getOperation().isUnwrapped();
        if (isFromWrapper
            && !Boolean.TRUE.equals(part.getProperty("messagepart.isheader"))) {
            UnwrappedOperationInfo uop = (UnwrappedOperationInfo)part.getMessageInfo().getOperation();
            OperationInfo op = uop.getWrappedOperation();
            MessageInfo inf = null;
            if (uop.getInput() == part.getMessageInfo()) {
                inf = op.getInput();
            } else if (uop.getOutput() == part.getMessageInfo()) {
                inf = op.getOutput();
            }
            if (inf != null
                && inf.getMessagePart(0).getTypeClass() != null) {
                //if the wrapper has a type class, we don't need to do anything
                //as everything would have been discovered when walking the
                //wrapper type (unless it's a header which wouldn't be in the wrapper)
                return;
            }
        }
        if (isFromWrapper
            && clazz.isArray()
            && !Byte.TYPE.equals(clazz.getComponentType())) {
            clazz = clazz.getComponentType();
        }
       
        Annotation[] a = (Annotation[])part.getProperty("parameter.annotations");
        checkForAdapter(clazz, a);

        Type genericType = (Type) part.getProperty("generic.type");
        if (genericType != null) {
            boolean isList = Collection.class.isAssignableFrom(clazz);
            if (isFromWrapper) {
                if (genericType instanceof Class
                    && ((Class<?>)genericType).isArray()) {
                    Class<?> cl2 = (Class<?>)genericType;
                    if (cl2.isArray()
                        && !Byte.TYPE.equals(cl2.getComponentType())) {
                        genericType = cl2.getComponentType();
                    }
                    addType(genericType);
                } else if (!isList) {
                    addType(genericType);
                }
            } else {
                addType(genericType, true);
            }

            if (isList
                && genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                if (pt.getActualTypeArguments().length > 0
                    && pt.getActualTypeArguments()[0] instanceof Class) {

                    Class<? extends Object> arrayCls =
                        Array.newInstance((Class<?>) pt.getActualTypeArguments()[0], 0).getClass();
                    clazz = arrayCls;
                    part.setTypeClass(clazz);
                    if (isFromWrapper) {
                        addType(clazz.getComponentType(), true);
                    }
                } else if (pt.getActualTypeArguments().length > 0
                    && pt.getActualTypeArguments()[0] instanceof GenericArrayType) {
                    GenericArrayType gat = (GenericArrayType)pt.getActualTypeArguments()[0];
                    gat.getGenericComponentType();
                    Class<? extends Object> arrayCls =
                        Array.newInstance((Class<?>) gat.getGenericComponentType(), 0).getClass();
                    clazz = Array.newInstance(arrayCls, 0).getClass();
                    part.setTypeClass(clazz);
                    if (isFromWrapper) {
                        addType(clazz.getComponentType(), true);
                    }
                }
            }
            if (isFromWrapper && isList) {
                clazz = null;
            }
        }
        if (clazz != null) {
            if (!isFromWrapper
                && clazz.getAnnotation(XmlRootElement.class) == null
                && clazz.getAnnotation(XmlType.class) != null
                && StringUtils.isEmpty(clazz.getAnnotation(XmlType.class).name())) {
                Object ref = JAXBClassLoaderUtils.createTypeReference(part.getName(), clazz);
                if (ref != null) {
                    typeReferences.add(ref);
                }
            }
           
            addClass(clazz);
        }
    }

    private void checkForAdapter(Class<?> clazz, Annotation[] anns) {
        if (anns != null) {
            for (Annotation a : anns) {
                if (XmlJavaTypeAdapter.class.isAssignableFrom(a.annotationType())) {
                    Type t = Utils.getTypeFromXmlAdapter((XmlJavaTypeAdapter)a);
                    if (t != null) {
                        addType(t);
                    }
                }
            }
        }
        XmlJavaTypeAdapter xjta = clazz.getAnnotation(XmlJavaTypeAdapter.class);
        if (xjta != null) {
            Type t = Utils.getTypeFromXmlAdapter(xjta);
            if (t != null) {
                addType(t);
            }
        }
        if (clazz.getPackage() != null) {
            XmlJavaTypeAdapters adapt = clazz.getPackage().getAnnotation(XmlJavaTypeAdapters.class);
            if (adapt != null) {
                for (XmlJavaTypeAdapter a: adapt.value()) {
                    globalAdapters.add(a.type());
                }
                for (XmlJavaTypeAdapter a: adapt.value()) {
                    Type t = Utils.getTypeFromXmlAdapter(a);
                    if (t != null) {
                        addType(t);
                    }
                }
            }
        }
    }

    private void addType(Type cls) {
        addType(cls, false);
    }
    private void addType(Type cls, boolean allowArray) {
        if (cls instanceof Class) {
            if (globalAdapters.contains(cls)) {
                return;
            }
            if (((Class<?>)cls).isArray() && !allowArray) {
                addClass(((Class<?>)cls).getComponentType());
            } else {
                addClass((Class<?>)cls);
            }
        } else if (cls instanceof ParameterizedType) {
            addType(((ParameterizedType)cls).getRawType());
            if (!((ParameterizedType)cls).getRawType().equals(Enum.class)) {
                for (Type t2 : ((ParameterizedType)cls).getActualTypeArguments()) {
                    addType(t2);
                }
            }
        } else if (cls instanceof GenericArrayType) {
            Class<?> ct;
            GenericArrayType gt = (GenericArrayType)cls;
            Type componentType = gt.getGenericComponentType();
            if (componentType instanceof Class) {
                ct = (Class<?>)componentType;
            } else {
                TypeVariable<?> tv = (TypeVariable<?>)componentType;
                Type[] bounds = tv.getBounds();
                if (bounds != null && bounds.length == 1) {
                    if (bounds[0] instanceof Class) {
                        ct = (Class<?>)bounds[0];
                    } else {
                        throw new IllegalArgumentException("Unable to determine type for: " + tv);
                    }
                } else {
                    throw new IllegalArgumentException("Unable to determine type for: " + tv);
                }
            }
            ct = Array.newInstance(ct, 0).getClass();

            addClass(ct);
        } else if (cls instanceof WildcardType) {
            for (Type t : ((WildcardType)cls).getUpperBounds()) {
                addType(t);
            }
            for (Type t : ((WildcardType)cls).getLowerBounds()) {
                addType(t);
            }
        } else if (cls instanceof TypeVariable) {
            for (Type t : ((TypeVariable<?>)cls).getBounds()) {
                addType(t);
            }
        }
    }

    void addClass(Class<?> claz) {
        if (Throwable.class.isAssignableFrom(claz)) {
            if (!Throwable.class.equals(claz)
                && !Exception.class.equals(claz)) {
                walkReferences(claz);
            }
            addClass(String.class);
        } else if (claz.getName().startsWith("java.")
            || claz.getName().startsWith("javax.")) {
            return;
        } else {
            Class<?> cls = JAXBUtils.getValidClass(claz);
            if (cls == null && ReflectionUtil.getDeclaredConstructors(claz).length > 0) {
                //there is no init(), but other constructors
                Object factory = createFactory(claz, ReflectionUtil.getDeclaredConstructors(claz)[0]);
                unmarshallerProperties.put("com.sun.xml.bind.ObjectFactory", factory);
                cls = claz;
            }
            if (null != cls) {
                if (classes.contains(cls)) {
                    return;
                }

                if (!cls.isInterface()) {
                    classes.add(cls);
                }

                XmlSeeAlso xsa = cls.getAnnotation(XmlSeeAlso.class);
                if (xsa != null) {
                    for (Class<?> c : xsa.value()) {
                        addClass(c);
                    }
                }
                XmlJavaTypeAdapter xjta = cls.getAnnotation(XmlJavaTypeAdapter.class);
                if (xjta != null) {
                    //has an adapter.   We need to inspect the adapter and then
                    //return as the adapter will handle the superclass
                    //and interfaces and such
                    Type t = Utils.getTypeFromXmlAdapter(xjta);
                    if (t != null) {
                        addType(t);
                    }
                    return;
                }
               
                if (cls.getSuperclass() != null) {
                    //JAXB should do this, but it doesn't always.
                    //in particular, older versions of jaxb don't
                    addClass(cls.getSuperclass());
                }

                if (!cls.isInterface()) {
                    walkReferences(cls);
                }
            }
        }
    }
    private void walkReferences(Class<?> cls) {
        if (cls == null) {
            return;
        }
        if (cls.getName().startsWith("java.")
            || cls.getName().startsWith("javax.")) {
            return;
        }
        //walk the public fields/methods to try and find all the classes. JAXB will only load the
        //EXACT classes in the fields/methods if they are in a different package. Thus,
        //subclasses won't be found and the xsi:type stuff won't work at all.
        //We'll grab the public field/method types and then add the ObjectFactory stuff
        //as well as look for jaxb.index files in those packages.

        XmlAccessType accessType = Utils.getXmlAccessType(cls);

        if (accessType != XmlAccessType.PROPERTY) {   // only look for fields if we are instructed to
            //fields are accessible even if not public, must look at the declared fields
            //then walk to parents declared fields, etc...
            Field fields[] = ReflectionUtil.getDeclaredFields(cls);
            for (Field f : fields) {
                if (isFieldAccepted(f, accessType)) {
                    XmlJavaTypeAdapter xjta = Utils.getFieldXJTA(f);
                    if (xjta != null) {
                        Type t = Utils.getTypeFromXmlAdapter(xjta);
                        if (t != null) {
                            addType(t);
                            continue;
                        }
                    }
                    addType(f.getGenericType());
                }
            }
            walkReferences(cls.getSuperclass());
        }

        if (accessType != XmlAccessType.FIELD) {   // only look for methods if we are instructed to
            Method methods[] = ReflectionUtil.getDeclaredMethods(cls);
            for (Method m : methods) {
                if (isMethodAccepted(m, accessType)) {
                    XmlJavaTypeAdapter xjta = Utils.getMethodXJTA(m);
                    if (xjta != null) {
                        Type t = Utils.getTypeFromXmlAdapter(xjta);
                        if (t != null) {
                            addType(t);
                            continue;
                        }
                    }
                    addType(m.getGenericReturnType());
                    for (Type t : m.getGenericParameterTypes()) {
                        addType(t);
                    }
                }
            }
        }
    }

    /**
     * Checks if the field is accepted as a JAXB property.
     */
    static boolean isFieldAccepted(Field field, XmlAccessType accessType) {
        // We only accept non static fields which are not marked @XmlTransient or has transient modifier
        if (field.isAnnotationPresent(XmlTransient.class)
            || Modifier.isTransient(field.getModifiers())) {
            return false;
        }
        if (Modifier.isStatic(field.getModifiers())) {
            return field.isAnnotationPresent(XmlAttribute.class);
        }
        if (accessType == XmlAccessType.PUBLIC_MEMBER
            && !Modifier.isPublic(field.getModifiers())) {
            return false;
        }
        if (accessType == XmlAccessType.NONE
            || accessType == XmlAccessType.PROPERTY) {
            return checkJaxbAnnotation(field.getAnnotations());
        } else {
            return true;
        }
    }

    /**
     * Checks if the method is accepted as a JAXB property getter.
     */
    static boolean isMethodAccepted(Method method, XmlAccessType accessType) {
        // We only accept non static property getters which are not marked @XmlTransient
        if (Modifier.isStatic(method.getModifiers())
                || method.isAnnotationPresent(XmlTransient.class)
                || !Modifier.isPublic(method.getModifiers())
                || "getClass".equals(method.getName())) {
            return false;
        }

        // must not have parameters and return type must not be void
        if (method.getReturnType() == Void.class
            || method.getParameterTypes().length != 0
            || (method.getDeclaringClass().equals(Throwable.class)
            && !("getMessage".equals(method.getName())))
            || !(method.getName().startsWith("get")
                    || method.getName().startsWith("is"))) {
            return false;
        }
        int beginIndex = 3;
        if (method.getName().startsWith("is")) {
            beginIndex = 2;
        }
       
        Method setter = null;
        try {
            setter = method.getDeclaringClass()
                .getMethod("set" + method.getName().substring(beginIndex),
                           new Class[] {method.getReturnType()});
        } catch (Exception e) {
            //getter, but no setter
        }
        if (setter != null) {
            if (setter.isAnnotationPresent(XmlTransient.class)
                || !Modifier.isPublic(setter.getModifiers())) {
                return false;
            }
        } else if (!Collection.class.isAssignableFrom(method.getReturnType())
            && !Throwable.class.isAssignableFrom(method.getDeclaringClass())) {
            //no setter, it's not a collection (thus getter().add(...)), and
            //not an Exception,
            return false;
        }

        if (accessType == XmlAccessType.NONE
            || accessType == XmlAccessType.FIELD) {
            return checkJaxbAnnotation(method.getAnnotations());
        }
        return true;
    }

    /**
     * Checks if there are JAXB annotations among the annotations of the class member.
     * @param annotations the array of annotations from the class member
     * @return true if JAXB annotations are present, false otherwise
     */
    static boolean checkJaxbAnnotation(Annotation[] annotations) {
        // must check if there are any jaxb annotations
        Package jaxbAnnotationsPackage = XmlElement.class.getPackage();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().getPackage() == jaxbAnnotationsPackage) {
                return true;
            }
        }
        return false;
    }
    @SuppressWarnings("unused")
    private Object createFactory(Class<?> cls, Constructor<?> contructor) {      
        String newClassName = cls.getName() + "Factory";
        ASMHelper helper = new ASMHelper();
        ClassWriter cw = helper.createClassWriter();
        MethodVisitor mv;

        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
                 ASMHelper.periodToSlashes(newClassName), null, "java/lang/Object", null);

        cw.visitSource(cls.getSimpleName() + "Factory" + ".java", null);

        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "create" + cls.getSimpleName(),
                            "()L" + ASMHelper.periodToSlashes(cls.getName()) + ";", null, null);
        mv.visitCode();
        String name = cls.getName().replace(".", "/");
        mv.visitTypeInsn(Opcodes.NEW, name);
        mv.visitInsn(Opcodes.DUP);
        StringBuilder paraString = new StringBuilder("(");
      
        for (Class<?> paraClass : contructor.getParameterTypes()) {
            mv.visitInsn(Opcodes.ACONST_NULL);
            paraString.append("Ljava/lang/Object;");     
        }
        paraString.append(")V");

        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, name, "<init>", paraString.toString());

        mv.visitInsn(Opcodes.ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        cw.visitEnd();
        Class<?> factoryClass = helper.loadClass(newClassName, cls, cw.toByteArray());
        try {
            return factoryClass.newInstance();
        } catch (Exception e) {
           //ignore
        }
        return null;
    }
   
   
}
TOP

Related Classes of org.apache.cxf.jaxb.JAXBContextInitializer

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.