All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.xfire.aegis.type.basic.BeanType Maven / Gradle / Ivy

package org.codehaus.xfire.aegis.type.basic;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.xml.namespace.QName;

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.XFireRuntimeException;
import org.codehaus.xfire.aegis.AegisBindingProvider;
import org.codehaus.xfire.aegis.MessageReader;
import org.codehaus.xfire.aegis.MessageWriter;
import org.codehaus.xfire.aegis.type.Type;
import org.codehaus.xfire.aegis.type.TypeMapping;
import org.codehaus.xfire.fault.XFireFault;
import org.codehaus.xfire.soap.SoapConstants;
import org.codehaus.xfire.util.ClassLoaderUtils;
import org.codehaus.xfire.util.NamespaceHelper;
import org.jdom.Attribute;
import org.jdom.Element;

/**
 * Serializes JavaBeans.
 * 
 * @author Dan Diephouse
 * @author Jack Hong
 */
public class BeanType
    extends Type
{
    private BeanTypeInfo _info;

    private boolean isInterface = false;

    private boolean isException = false;

    public BeanType()
    {
    }

    public BeanType(BeanTypeInfo info)
    {
        this._info = info;
        this.setTypeClass(info.getTypeClass());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.codehaus.xfire.aegis.type.Type#readObject(org.codehaus.xfire.aegis.MessageReader,
     *      org.codehaus.xfire.MessageContext)
     */
    public Object readObject(MessageReader reader, MessageContext context)
        throws XFireFault
    {
        BeanTypeInfo info = getTypeInfo();

        try
        {
            Class clazz = getTypeClass();
            Object object = null;
            InterfaceInvocationHandler delegate = null;
            boolean isProxy = false;

            if (isInterface)
            {
                String impl = null;
                if (context.getService() != null)
                {
                    impl = (String) context.getService().getProperty(clazz.getName()
                            + ".implementation");
                }

                if (impl == null)
                {
                    delegate = new InterfaceInvocationHandler();
                    object = Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                                    new Class[] { clazz },
                                                    delegate);
                    isProxy = true;
                }
                else
                {
                    try
                    {
                        clazz = ClassLoaderUtils.loadClass(impl, getClass());
                        object = clazz.newInstance();
                    }
                    catch (ClassNotFoundException e)
                    {
                        throw new XFireRuntimeException("Could not find implementation class "
                                + impl + " for class " + clazz.getName());
                    }
                }
            }
            else if (isException)
            {
                object = createFromFault(context);
            }
            else
            {
                object = clazz.newInstance();
            }

            // Read attributes
            while (reader.hasMoreAttributeReaders())
            {
                MessageReader childReader = reader.getNextAttributeReader();
                QName name = childReader.getName();

                Type type = info.getType(name);

                if (type != null)
                {
                    Object writeObj = type.readObject(childReader, context);
                    if (isProxy)
                    {
                        delegate.writeProperty(name.getLocalPart(), writeObj);
                    }
                    else
                    {
                        writeProperty(name, object, writeObj, clazz, info);
                    }
                }
            }

            // Read child elements
            while (reader.hasMoreElementReaders())
            {
                MessageReader childReader = reader.getNextElementReader();
                QName name = childReader.getName();

                BeanType parent = getBeanTypeWithProperty(name);
                Type defaultType = null;
                if (parent != null)
                {
                    info = parent.getTypeInfo();
                    defaultType = info.getType(name);
                }
                else
                {
                	defaultType = null;
                }
                
                Type type = AegisBindingProvider.getReadType(childReader.getXMLStreamReader(),
                		context, defaultType, getTypeMapping());
                
                if (type != null)
                {
                    if (!childReader.isXsiNil())
                    {
                        Object writeObj = type.readObject(childReader, context);

                        if (isProxy)
                        {
                            delegate.writeProperty(name.getLocalPart(), writeObj);
                        }
                        else
                        {
                            writeProperty(name, object, writeObj, clazz, info);
                        }
                    }
                    else
                    {
                        if (!info.isNillable(name))
                        {
                            throw new XFireFault(
                                    name.getLocalPart() + " is nil, but not nillable.",
                                    XFireFault.SENDER);

                        }
                        childReader.readToEnd();
                    }
                }
                else
                {
                    childReader.readToEnd();
                }
            }

            return object;
        }
        catch (IllegalAccessException e)
        {
            throw new XFireFault("Illegal access. " + e.getMessage(), e, XFireFault.RECEIVER);
        }
        catch (InstantiationException e)
        {
            throw new XFireFault("Couldn't instantiate class. " + e.getMessage(), e,
                    XFireFault.SENDER);
        }
        catch (SecurityException e)
        {
            throw new XFireFault("Illegal access. " + e.getMessage(), e, XFireFault.RECEIVER);
        }
        catch (IllegalArgumentException e)
        {
            throw new XFireFault("Illegal argument. " + e.getMessage(), e, XFireFault.RECEIVER);
        }
        catch (InvocationTargetException e)
        {
            throw new XFireFault("Couldn't create class: " + e.getMessage(), e, XFireFault.RECEIVER);
        }

    }

    /**
     * If the class is an exception, this will try and instantiate it with
     * information from the XFireFault (if it exists).
     */
    protected Object createFromFault(MessageContext context)
        throws SecurityException, InstantiationException, IllegalAccessException,
        IllegalArgumentException, InvocationTargetException
    {
        Class clazz = getTypeClass();
        Constructor ctr;
        Object o;
        Object body = context.getExchange().getFaultMessage().getBody();

        if (!(body instanceof XFireFault))
            return clazz.newInstance();

        XFireFault fault = (XFireFault) body;

        try
        {
            ctr = clazz.getConstructor(new Class[] { String.class, Throwable.class });
            o = ctr.newInstance(new Object[] { fault.getMessage(), fault });
        }
        catch (NoSuchMethodException e)
        {
            try
            {
                ctr = clazz.getConstructor(new Class[] { String.class, Exception.class });
                o = ctr.newInstance(new Object[] { fault.getMessage(), fault });
            }
            catch (NoSuchMethodException e1)
            {
                try
                {
                    ctr = clazz.getConstructor(new Class[] { String.class });
                    o = ctr.newInstance(new Object[] { fault.getMessage() });
                }
                catch (NoSuchMethodException e2)
                {
                    return clazz.newInstance();
                }
            }
        }

        return o;
    }

    /**
     * Write the specified property to a field.
     */
    protected void writeProperty(QName name,
                                 Object object,
                                 Object property,
                                 Class impl,
                                 BeanTypeInfo info)
        throws XFireFault
    {
        try
        {
            PropertyDescriptor desc = info.getPropertyDescriptorFromMappedName(name);

            Method m = desc.getWriteMethod();

            if (m == null)
            {
                if (getTypeClass().isInterface())
                    m = getWriteMethodFromImplClass(impl, desc);

                if (m == null)
                    throw new XFireFault("No write method for property " + name + " in "
                            + object.getClass(), XFireFault.SENDER);
            }

            Class propertyType = desc.getPropertyType();
            if ((property == null && !propertyType.isPrimitive()) || (property != null))
            {
                m.invoke(object, new Object[] { property });
            }
        }
        catch (Exception e)
        {
            if (e instanceof XFireFault)
                throw (XFireFault) e;

            throw new XFireFault("Couldn't set property " + name + " on " + object + ". "
                    + e.getMessage(), e, XFireFault.SENDER);
        }
    }

    /**
     * This is a hack to get the write method from the implementation class for
     * an interface.
     */
    private Method getWriteMethodFromImplClass(Class impl, PropertyDescriptor pd)
        throws Exception
    {
        String name = pd.getName();
        name = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);

        return impl.getMethod(name, new Class[] { pd.getPropertyType() });
    }

    /**
     * @see org.codehaus.xfire.aegis.type.Type#writeObject(Object,
     *      org.codehaus.xfire.aegis.MessageWriter,
     *      org.codehaus.xfire.MessageContext)
     */
    public void writeObject(Object object, MessageWriter writer, MessageContext context)
        throws XFireFault
    {
        if (object == null)
            return;

        BeanTypeInfo info = getTypeInfo();

        if (context.getService() != null)
        {
            Object writeXsiType = context.getService()
                    .getProperty(AegisBindingProvider.WRITE_XSI_TYPE_KEY);
            if ((Boolean.TRUE.equals(writeXsiType) || "true".equals(writeXsiType))
                && object.getClass() == getTypeClass())
            {
                writer.writeXsiType(getSchemaType());
            }

        }

        /*
         * TODO: Replace this method with one split into two pieces so that we
         * can front-load the attributes and traverse down the list of super
         * classes.
         */
        for (Iterator itr = info.getAttributes(); itr.hasNext();)
        {
            QName name = (QName) itr.next();

            Object value = readProperty(object, name);
            if (value != null)
            {
                Type type = getType(info, name);

                if (type == null)
                    throw new XFireRuntimeException("Couldn't find type for " + value.getClass()
                            + " for property " + name);

                MessageWriter cwriter = writer.getAttributeWriter(name);

                type.writeObject(value, cwriter, context);

                cwriter.close();
            }
        }

        for (Iterator itr = info.getElements(); itr.hasNext();)
        {
            QName name = (QName) itr.next();

            if (info.isExtension()
                    && info.getPropertyDescriptorFromMappedName(name).getReadMethod()
                            .getDeclaringClass() != info.getTypeClass())
            {
                continue;
            }
            Object value = readProperty(object, name);

            Type type = getType(info, name);
            type = AegisBindingProvider.getWriteType(context, value, type);
            MessageWriter cwriter;

            // Write the value if it is not null.
            if (value != null)
            {
                cwriter = getWriter(writer, name, type);

                if (type == null)
                    throw new XFireRuntimeException("Couldn't find type for " + value.getClass()
                            + " for property " + name);

                type.writeObject(value, cwriter, context);

                cwriter.close();
            }
            else if (info.isNillable(name))
            {
                cwriter = getWriter(writer, name, type);

                // Write the xsi:nil if it is null.
                cwriter.writeXsiNil();

                cwriter.close();
            }
        }
        if (info.isExtension())
        {
            Type t = getSuperType();
            if (t != null)
            {
                t.writeObject(object, writer, context);
            }
        }
    }

    private MessageWriter getWriter(MessageWriter writer, QName name, Type type)
    {
        MessageWriter cwriter;
        if (type.isAbstract())
        {
            cwriter = writer.getElementWriter(name);
        }
        else
        {
            cwriter = writer.getElementWriter(name);
        }
        return cwriter;
    }

    protected Object readProperty(Object object, QName name)
    {
        try
        {
            PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(name);

            Method m = desc.getReadMethod();

            if (m == null)
                throw new XFireFault("No read method for property " + name + " in class "
                        + object.getClass().getName(), XFireFault.SENDER);

            return m.invoke(object, new Object[0]);
        }
        catch (Exception e)
        {
            throw new XFireRuntimeException("Couldn't get property " + name + " from bean "
                    + object, e);
        }
    }

    /**
     * @see org.codehaus.xfire.aegis.type.Type#writeSchema(org.jdom.Element)
     */
    public void writeSchema(Element root)
    {
        BeanTypeInfo info = getTypeInfo();
        Element complex = new Element("complexType", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
        complex.setAttribute(new Attribute("name", getSchemaType().getLocalPart()));
        root.addContent(complex);

        Type sooperType = getSuperType();

        /*
         * See Java Virtual Machine specification:
         * http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#75734
         */
        if (((info.getTypeClass().getModifiers() & Modifier.ABSTRACT) != 0) &&
                !info.getTypeClass().isInterface())
        {
            complex.setAttribute(new Attribute("abstract", "true"));
        }

        if (info.isExtension() && sooperType != null)
        {
            Element complexContent = new Element("complexContent", SoapConstants.XSD_PREFIX,
                    SoapConstants.XSD);
            complex.addContent(complexContent);
            complex = complexContent;
        }

        /*
         * Decide if we're going to extend another type. If we are going to
         * defer, then make sure that we extend the type for our superclass.
         */
        boolean isExtension = info.isExtension();

        Element dummy = complex;

        if (isExtension && sooperType != null)
        {

            Element extension = new Element("extension", SoapConstants.XSD_PREFIX,
                    SoapConstants.XSD);
            complex.addContent(extension);
            QName baseType = sooperType.getSchemaType();
            extension.setAttribute(new Attribute("base", getNameWithPrefix2(root, baseType
                    .getNamespaceURI(), baseType.getLocalPart())));

            dummy = extension;
        }

        Element seq = null;

        // Write out schema for elements
        for (Iterator itr = info.getElements(); itr.hasNext();)
        {

            QName name = (QName) itr.next();

            if (isExtension)
            {
                PropertyDescriptor pd = info.getPropertyDescriptorFromMappedName(name);

                assert pd.getReadMethod() != null && pd.getWriteMethod() != null;
                if (pd.getReadMethod().getDeclaringClass() != info.getTypeClass())
                {
                    continue;
                }
            }

            if (seq == null)
            {
                seq = new Element("sequence", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
                dummy.addContent(seq);
            }

            Element element = new Element("element", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
            seq.addContent(element);

            Type type = getType(info, name);

            String nameNS = name.getNamespaceURI();
            String nameWithPrefix = getNameWithPrefix(root, nameNS, name.getLocalPart());

            String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), type
                    .getSchemaType().getNamespaceURI());

            writeTypeReference(name, nameWithPrefix, element, type, prefix);
        }

        /**
         * if future proof then add  element
         */
        if (info.isExtensibleElements())
        {
            if (seq == null)
            {
                seq = new Element("sequence", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
                dummy.addContent(seq);
            }
            seq.addContent(createAnyElement());
        }

        // Write out schema for attributes
        for (Iterator itr = info.getAttributes(); itr.hasNext();)
        {
            QName name = (QName) itr.next();

            Element element = new Element("attribute", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
            dummy.addContent(element);

            Type type = getType(info, name);

            String nameNS = name.getNamespaceURI();
            String nameWithPrefix = getNameWithPrefix(root, nameNS, name.getLocalPart());

            String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), type
                    .getSchemaType().getNamespaceURI());
            element.setAttribute(new Attribute("name", nameWithPrefix));
            element.setAttribute(new Attribute("type", prefix + ':'
                    + type.getSchemaType().getLocalPart()));
        }

        /**
         * If extensible attributes then add 
         */
        if (info.isExtensibleAttributes())
        {
            dummy.addContent(createAnyAttribute());
        }
    }

    private String getNameWithPrefix(Element root, String nameNS, String localName)
    {
        if (!nameNS.equals(getSchemaType().getNamespaceURI()))
        {
            String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), nameNS);

            if (prefix == null || prefix.length() == 0)
                prefix = NamespaceHelper.getUniquePrefix(root, nameNS);

            return prefix + ":" + localName;
        }
        return localName;
    }

    private String getNameWithPrefix2(Element root, String nameNS, String localName)
    {
        String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), nameNS);

        if (prefix == null || prefix.length() == 0)
            prefix = NamespaceHelper.getUniquePrefix(root, nameNS);

        return prefix + ":" + localName;
    }

    private Type getType(BeanTypeInfo info, QName name)
    {
        Type type = info.getType(name);

        if (type == null)
        {
            throw new NullPointerException("Couldn't find type for" + name + " in class "
                    + getTypeClass().getName());
        }

        return type;
    }

    private void writeTypeReference(QName name,
                                    String nameWithPrefix,
                                    Element element,
                                    Type type,
                                    String prefix)
    {
        if (type.isAbstract())
        {
            element.setAttribute(new Attribute("name", nameWithPrefix));
            element.setAttribute(new Attribute("type", prefix + ':'
                    + type.getSchemaType().getLocalPart()));

            int minOccurs = getTypeInfo().getMinOccurs(name);
            if (minOccurs != 1)
            {
                element.setAttribute(new Attribute("minOccurs", new Integer(minOccurs).toString()));
            }

            if (getTypeInfo().isNillable(name))
            {
                element.setAttribute(new Attribute("nillable", "true"));
            }
        }
        else
        {
            element.setAttribute(new Attribute("ref", prefix + ':'
                    + type.getSchemaType().getLocalPart()));
        }
    }

    public void setTypeClass(Class typeClass)
    {
        super.setTypeClass(typeClass);

        isInterface = typeClass.isInterface();
        isException = Exception.class.isAssignableFrom(typeClass);
    }

    /**
     * We need to write a complex type schema for Beans, so return true.
     * 
     * @see org.codehaus.xfire.aegis.type.Type#isComplex()
     */
    public boolean isComplex()
    {
        return true;
    }

    public Set getDependencies()
    {
        Set deps = new HashSet();

        BeanTypeInfo info = getTypeInfo();

        for (Iterator itr = info.getAttributes(); itr.hasNext();)
        {
            QName name = (QName) itr.next();
            deps.add(info.getType(name));
        }

        for (Iterator itr = info.getElements(); itr.hasNext();)
        {
            QName name = (QName) itr.next();
            if (info.isExtension()
                    && info.getPropertyDescriptorFromMappedName(name).getReadMethod()
                            .getDeclaringClass() != info.getTypeClass())
                continue;
            deps.add(info.getType(name));
        }

        /*
         * Automagically add chain of superclasses *if* this is an an extension.
         */
        if (info.isExtension())
        {
            Type sooperType = getSuperType();
            if (sooperType != null)
            {
                deps.add(sooperType);
            }
        }

        return deps;
    }

    private BeanType getBeanTypeWithProperty(QName name)
    {
        BeanType sooper = this;
        Type type = null;

        while (type == null && sooper != null)
        {
            type = sooper.getTypeInfo().getType(name);

            if (type == null)
                sooper = sooper.getSuperType();
        }

        return (BeanType) sooper;
    }

    private BeanType getSuperType()
    {
        BeanTypeInfo info = getTypeInfo();
        Class c = info.getTypeClass().getSuperclass();
        /*
         * Don't dig any deeper than Object or Exception
         */
        if (c != null && c != Object.class && c != Exception.class && c != RuntimeException.class)
        {
            TypeMapping tm = info.getTypeMapping();
            BeanType superType = (BeanType) tm.getType(c);
            if (superType == null)
            {
                superType = (BeanType) getTypeMapping().getTypeCreator().createType(c);
                Class cParent = c.getSuperclass();
                if (cParent != null && cParent != Object.class)
                {
                    superType.getTypeInfo().setExtension(true);
                }
                tm.register(superType);
            }
            return superType;
        }
        else
        {
            return null;
        }
    }

    public BeanTypeInfo getTypeInfo()
    {
        if (_info == null)
        {
            _info = createTypeInfo();
        }

        // Delay initialization so things work in recursive scenarios
        _info.initialize();

        return _info;
    }

    public BeanTypeInfo createTypeInfo()
    {
        BeanTypeInfo info = new BeanTypeInfo(getTypeClass(), getSchemaType().getNamespaceURI());

        info.setTypeMapping(getTypeMapping());
        info.initialize();

        return info;
    }

    /**
     * Create an element to represent any future elements that might get added
     * to the schema 
     * 
     * @return
     */
    private Element createAnyElement()
    {
        Element result = new Element("any", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
        result.setAttribute(new Attribute("minOccurs", "0"));
        result.setAttribute(new Attribute("maxOccurs", "unbounded"));
        return result;
    }

    public String toString()
    {
        StringBuffer sb = new StringBuffer();
        sb.append(getClass().getName());
        sb.append(": [class=");
        Class c = getTypeClass();
        sb.append((c == null) ? ("") : (c.getName()));
        sb.append(",\nQName=");
        QName q = getSchemaType();
        sb.append((q == null) ? ("") : (q.toString()));
        sb.append(",\ninfo=");
        sb.append(getTypeInfo().toString());
        sb.append("]");
        return sb.toString();
    }

    /**
     * Create an element to represent any future attributes that might get added
     * to the schema 
     * 
     * @return
     */
    private Element createAnyAttribute()
    {
        Element result = new Element("anyAttribute", SoapConstants.XSD_PREFIX, SoapConstants.XSD);
        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy