org.apache.cxf.aegis.type.basic.BeanType Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cxf-bundle-minimal Show documentation
Show all versions of cxf-bundle-minimal Show documentation
Apache CXF Minimal Bundle Jar
/**
* 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.aegis.type.basic;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;
import org.apache.cxf.aegis.AegisContext;
import org.apache.cxf.aegis.Context;
import org.apache.cxf.aegis.DatabindingException;
import org.apache.cxf.aegis.type.AbstractTypeCreator;
import org.apache.cxf.aegis.type.AegisType;
import org.apache.cxf.aegis.type.TypeMapping;
import org.apache.cxf.aegis.type.TypeUtil;
import org.apache.cxf.aegis.type.mtom.AbstractXOPType;
import org.apache.cxf.aegis.xml.MessageReader;
import org.apache.cxf.aegis.xml.MessageWriter;
import org.apache.cxf.common.classloader.ClassLoaderUtils;
import org.apache.cxf.common.xmlschema.XmlSchemaUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAny;
import org.apache.ws.commons.schema.XmlSchemaAnyAttribute;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaComplexContent;
import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaSequence;
/**
* Serializes JavaBeans.
*
* There's a really dangerous coding convention in this class, maintainers beware.
* There are two constructor. The no-args constructor defers, until later,
* the construction of a BeanTypeInfo. The one-arg constructor gets the BeanTypeInfo passed as a parameter.
* Aegis doesn't have any uniform discipline of 'construct, set properties, initialize'. Instead,
* each piece of code that uses the type info needs to call getTypeInfo() instead of referencing the
* 'info' field.
*/
public class BeanType extends AegisType {
private BeanTypeInfo info;
private boolean isInterface;
private boolean isException;
/**
* Construct a type info. Caller must pass in the type class via
* setTypeClass later.
*/
public BeanType() {
}
/**
* Construct a type info given a full BeanTypeInfo.
* @param info
*/
public BeanType(BeanTypeInfo info) {
this.info = info;
this.typeClass = info.getTypeClass();
initTypeClass();
}
private void initTypeClass() {
// throw if someone tries to set up a generic bean.
Class plainClass = (Class) typeClass;
this.isInterface = plainClass.isInterface();
isException = Exception.class.isAssignableFrom(plainClass);
}
/**
* {@inheritDoc}
*/
@Override
public Object readObject(MessageReader reader, Context context) throws DatabindingException {
BeanTypeInfo inf = getTypeInfo();
try {
Class clazz = getTypeClass();
Object object;
// the target for properties; either the object or the proxy handler
Object target;
if (isInterface) {
String impl = context.getGlobalContext().getBeanImplementationMap().get(clazz);
if (impl == null) {
InvocationHandler handler = new InterfaceInvocationHandler();
object = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {
clazz
}, handler);
target = handler;
} else {
try {
clazz = ClassLoaderUtils.loadClass(impl, getClass());
object = clazz.newInstance();
target = object;
} catch (ClassNotFoundException e) {
throw new DatabindingException("Could not find implementation class " + impl
+ " for class " + clazz.getName());
}
}
} else if (isException) {
object = createFromFault(context);
target = object;
} else {
object = clazz.newInstance();
target = object;
}
// Read attributes
while (reader.hasMoreAttributeReaders()) {
MessageReader childReader = reader.getNextAttributeReader();
QName name = childReader.getName();
AegisType type = inf.getType(name);
if (type != null) {
Object writeObj = type.readObject(childReader, context);
writeProperty(name, target, writeObj, clazz, inf);
}
}
// Read child elements
while (reader.hasMoreElementReaders()) {
MessageReader childReader = reader.getNextElementReader();
QName name = childReader.getName();
// Find the BeanTypeInfo that contains a property for the element name
BeanTypeInfo propertyTypeInfo = getBeanTypeInfoWithProperty(name);
// Get the AegisType for the property
AegisType type = getElementType(name, propertyTypeInfo, childReader, context);
if (type != null) {
if (!childReader.isXsiNil()) {
Object writeObj;
if (type.isFlatArray()) {
ArrayType aType = (ArrayType) type;
PropertyDescriptor desc = inf.getPropertyDescriptorFromMappedName(name);
boolean isList = List.class.isAssignableFrom(desc.getPropertyType());
writeObj = aType.readObject(childReader, name, context, !isList);
} else {
writeObj = type.readObject(childReader, context);
}
writeProperty(name, target, writeObj, clazz, propertyTypeInfo);
} else {
if (!alwaysAllowNillables() && !propertyTypeInfo.isNillable(name)) {
throw new DatabindingException(name.getLocalPart()
+ " is nil, but not nillable.");
}
childReader.readToEnd();
}
} else {
childReader.readToEnd();
}
}
return object;
} catch (IllegalAccessException e) {
throw new DatabindingException("Illegal access. " + e.getMessage(), e);
} catch (InstantiationException e) {
throw new DatabindingException("Couldn't instantiate class. " + e.getMessage(), e);
} catch (SecurityException e) {
throw new DatabindingException("Illegal access. " + e.getMessage(), e);
} catch (IllegalArgumentException e) {
throw new DatabindingException("Illegal argument. " + e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new DatabindingException("Could not create class: " + e.getMessage(), e);
}
}
protected boolean alwaysAllowNillables() {
return false;
}
protected AegisType getElementType(QName name, BeanTypeInfo beanTypeInfo,
MessageReader reader, Context context) {
AegisType type = beanTypeInfo.getType(name);
// AegisType can be overriden with a xsi:type attribute
type = TypeUtil.getReadType(reader.getXMLStreamReader(), context.getGlobalContext(), type);
return type;
}
/**
* If the class is an exception, this will try and instantiate it with information from the XFireFault (if
* it exists).
*/
protected Object createFromFault(Context context) throws SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clazz = getTypeClass();
Constructor ctr;
Object o;
Fault fault = context.getFault();
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 inf)
throws DatabindingException {
if (object instanceof InterfaceInvocationHandler) {
InterfaceInvocationHandler delegate = (InterfaceInvocationHandler)object;
delegate.writeProperty(name.getLocalPart(), property);
return;
}
try {
PropertyDescriptor desc = inf.getPropertyDescriptorFromMappedName(name);
Method m = desc.getWriteMethod();
if (m == null) {
if (getTypeClass().isInterface()) {
m = getWriteMethodFromImplClass(impl, desc);
}
if (m == null && property instanceof List) {
m = desc.getReadMethod();
List