blob: 4d177e94511d500a095d5bd842ea835666cfcad6 [file] [log] [blame]
/**
* 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.io.InputStream;
import java.io.OutputStream;
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.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Logger;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessOrder;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorOrder;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.bind.attachment.AttachmentUnmarshaller;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.apache.cxf.common.i18n.Message;
import org.apache.cxf.common.jaxb.JAXBUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.ReflectionUtil;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.service.model.MessagePartInfo;
import org.apache.cxf.service.model.SchemaInfo;
import org.apache.cxf.staxutils.DepthXMLStreamReader;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.cxf.staxutils.W3CDOMStreamWriter;
import org.apache.cxf.staxutils.W3CNamespaceContext;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeList;
import org.apache.ws.commons.schema.constants.Constants;
/**
* Utility functions for JAXB.
*/
public final class JAXBEncoderDecoder {
private static final class AddXSITypeStreamReader extends StreamReaderDelegate {
private boolean first = true;
private int offset = 1;
private final QName typeQName;
private AddXSITypeStreamReader(XMLStreamReader reader, QName typeQName) {
super(reader);
this.typeQName = typeQName;
}
public int getAttributeCount() {
return super.getAttributeCount() + offset;
}
public String getAttributeLocalName(int index) {
if (first && index == 0) {
return "type";
}
return super.getAttributeLocalName(index - offset);
}
public QName getAttributeName(int index) {
if (first && index == 0) {
return new QName(Constants.URI_2001_SCHEMA_XSI, "type");
}
return super.getAttributeName(index - offset);
}
public String getAttributeNamespace(int index) {
if (first && index == 0) {
return Constants.URI_2001_SCHEMA_XSI;
}
return super.getAttributeNamespace(index - offset);
}
public String getAttributePrefix(int index) {
if (first && index == 0) {
return "xsi";
}
return super.getAttributePrefix(index - offset);
}
public String getAttributeType(int index) {
if (first && index == 0) {
return "#TEXT";
}
return super.getAttributeType(index - offset);
}
public String getAttributeValue(int index) {
if (first && index == 0) {
String pfx = this.getNamespaceContext().getPrefix(typeQName.getNamespaceURI());
if (StringUtils.isEmpty(pfx)) {
return typeQName.getLocalPart();
}
return pfx + ":" + typeQName.getLocalPart();
}
return super.getAttributeValue(index - offset);
}
public int next() throws XMLStreamException {
first = false;
offset = 0;
return super.next();
}
public String getAttributeValue(String namespaceUri,
String localName) {
if (first
&& Constants.URI_2001_SCHEMA_XSI.equals(namespaceUri)
&& "type".equals(localName)) {
String pfx = this.getNamespaceContext().getPrefix(typeQName.getNamespaceURI());
if (StringUtils.isEmpty(pfx)) {
return typeQName.getLocalPart();
}
return pfx + ":" + typeQName.getLocalPart();
}
return super.getAttributeValue(namespaceUri, localName);
}
}
private static final Logger LOG = LogUtils.getLogger(JAXBEncoderDecoder.class);
private JAXBEncoderDecoder() {
}
public static void marshall(Marshaller marshaller,
Object elValue,
MessagePartInfo part,
Object source) {
try {
// The Marshaller.JAXB_FRAGMENT will tell the Marshaller not to
// generate the xml declaration.
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
} catch (javax.xml.bind.PropertyException e) {
// intentionally empty.
}
Class<?> cls = null;
if (part != null) {
cls = part.getTypeClass();
}
if (cls == null) {
cls = null != elValue ? elValue.getClass() : null;
}
if (cls != null && cls.isArray() && elValue instanceof Collection) {
Collection<?> col = (Collection<?>)elValue;
elValue = col.toArray((Object[])Array.newInstance(cls.getComponentType(), col.size()));
}
try {
Object mObj = elValue;
QName elName = null;
if (part != null) {
elName = part.getConcreteName();
}
if (null != elName) {
if (part != null && part.getXmlSchema() instanceof XmlSchemaElement) {
XmlSchemaElement el = (XmlSchemaElement)part.getXmlSchema();
if (mObj.getClass().isArray()
&& el.getSchemaType() instanceof XmlSchemaSimpleType
&& ((XmlSchemaSimpleType)el.getSchemaType()).
getContent() instanceof XmlSchemaSimpleTypeList) {
mObj = Arrays.asList((Object[])mObj);
writeObject(marshaller, source, newJAXBElement(elName, cls, mObj));
} else if (part.getMessageInfo().getOperation().isUnwrapped()
&& (mObj.getClass().isArray() || mObj instanceof List)
&& el.getMaxOccurs() != 1) {
writeArrayObject(marshaller,
source,
elName,
mObj);
} else {
writeObject(marshaller, source, newJAXBElement(elName, cls, mObj));
}
} else if (byte[].class == cls && part.getTypeQName() != null
&& "hexBinary".equals(part.getTypeQName().getLocalPart())) {
mObj = new HexBinaryAdapter().marshal((byte[])mObj);
writeObject(marshaller, source, newJAXBElement(elName, String.class, mObj));
} else if (mObj instanceof JAXBElement) {
writeObject(marshaller, source, mObj);
} else if (marshaller.getSchema() != null) {
//force xsi:type so types can be validated instead of trying to
//use the RPC/lit element names that aren't in the schema
writeObject(marshaller, source, newJAXBElement(elName, Object.class, mObj));
} else {
writeObject(marshaller, source, newJAXBElement(elName, cls, mObj));
}
} else {
writeObject(marshaller, source, mObj);
}
} catch (Fault ex) {
throw ex;
} catch (javax.xml.bind.MarshalException ex) {
Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
.getMessage());
throw new Fault(faultMessage, ex);
} catch (Exception ex) {
throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static JAXBElement<?> newJAXBElement(QName elName, Class<?> cls, Object mObj) {
if (mObj instanceof JAXBElement) {
return (JAXBElement)mObj;
}
if (cls == null && mObj != null) {
cls = mObj.getClass();
}
return new JAXBElement(elName, cls, mObj);
}
//TODO: cache the JAXBRIContext
public static void marshalWithBridge(QName qname,
Class<?> cls,
Annotation[] anns,
Set<Class<?>> ctxClasses,
Object elValue,
Object source, AttachmentMarshaller am) {
try {
JAXBUtils.BridgeWrapper bridge = JAXBUtils.createBridge(ctxClasses, qname, cls, anns);
if (source instanceof XMLStreamWriter) {
bridge.marshal(elValue, (XMLStreamWriter)source, am);
} else if (source instanceof OutputStream) {
//the namespace is missing when marshal the xsd:QName type
//to the OutputStream directly
java.io.StringWriter sw = new java.io.StringWriter();
StreamResult s1 = new StreamResult(sw);
bridge.marshal(elValue, s1);
((OutputStream)source).write(sw.toString().getBytes());
} else if (source instanceof Node) {
bridge.marshal(elValue, (Node)source, am);
} else {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
} catch (javax.xml.bind.MarshalException ex) {
Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
.getMessage());
throw new Fault(faultMessage, ex);
} catch (Exception ex) {
throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
}
}
// TODO: cache the JAXBRIContext
public static Object unmarshalWithBridge(QName qname,
Class<?> cls,
Annotation[] anns,
Set<Class<?>> ctxClasses,
Object source,
AttachmentUnmarshaller am) {
try {
JAXBUtils.BridgeWrapper bridge = JAXBUtils.createBridge(ctxClasses, qname, cls, anns);
if (source instanceof XMLStreamReader) {
//DOMUtils.writeXml(StaxUtils.read((XMLStreamReader)source), System.out);
return bridge.unmarshal((XMLStreamReader)source, am);
} else if (source instanceof InputStream) {
return bridge.unmarshal((InputStream)source);
} else if (source instanceof Node) {
return bridge.unmarshal((Node)source, am);
} else {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
} catch (javax.xml.bind.MarshalException ex) {
Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
.getMessage());
throw new Fault(faultMessage, ex);
} catch (Exception ex) {
throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
}
}
public static void marshallException(Marshaller marshaller, Exception elValue,
MessagePartInfo part, Object source) {
XMLStreamWriter writer = getStreamWriter(source);
QName qn = part.getElementQName();
try {
writer.writeStartElement("ns1", qn.getLocalPart(), qn.getNamespaceURI());
Class<?> cls = part.getTypeClass();
XmlAccessType accessType = Utils.getXmlAccessType(cls);
String namespace = part.getElementQName().getNamespaceURI();
String attNs = namespace;
SchemaInfo sch = part.getMessageInfo().getOperation().getInterface()
.getService().getSchema(namespace);
if (sch == null) {
LOG.warning("Schema associated with " + namespace + " is null");
namespace = null;
attNs = null;
} else {
if (!sch.isElementFormQualified()) {
namespace = null;
}
if (!sch.isAttributeFormQualified()) {
attNs = null;
}
}
List<Member> combinedMembers = new ArrayList<>();
for (Field f : Utils.getFields(cls, accessType)) {
XmlAttribute at = f.getAnnotation(XmlAttribute.class);
if (at == null) {
combinedMembers.add(f);
} else {
QName fname = new QName(attNs, StringUtils.isEmpty(at.name()) ? f.getName() : at.name());
ReflectionUtil.setAccessible(f);
Object o = Utils.getFieldValue(f, elValue);
DocumentFragment frag = DOMUtils.getEmptyDocument().createDocumentFragment();
writeObject(marshaller, frag, newJAXBElement(fname, String.class, o));
if (attNs != null) {
writer.writeAttribute(attNs, fname.getLocalPart(),
DOMUtils.getAllContent(frag));
} else {
writer.writeAttribute(fname.getLocalPart(), DOMUtils.getAllContent(frag));
}
}
}
for (Method m : Utils.getGetters(cls, accessType)) {
if (!m.isAnnotationPresent(XmlAttribute.class)) {
combinedMembers.add(m);
} else {
int idx = m.getName().startsWith("get") ? 3 : 2;
String name = m.getName().substring(idx);
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
XmlAttribute at = m.getAnnotation(XmlAttribute.class);
QName mname = new QName(namespace, StringUtils.isEmpty(at.name()) ? name : at.name());
DocumentFragment frag = DOMUtils.getEmptyDocument().createDocumentFragment();
Object o = Utils.getMethodValue(m, elValue);
writeObject(marshaller, frag, newJAXBElement(mname, String.class, o));
if (attNs != null) {
writer.writeAttribute(attNs, mname.getLocalPart(),
DOMUtils.getAllContent(frag));
} else {
writer.writeAttribute(mname.getLocalPart(), DOMUtils.getAllContent(frag));
}
}
}
XmlAccessorOrder xmlAccessorOrder = cls.getAnnotation(XmlAccessorOrder.class);
if (xmlAccessorOrder != null && xmlAccessorOrder.value().equals(XmlAccessOrder.ALPHABETICAL)) {
Collections.sort(combinedMembers, new Comparator<Member>() {
public int compare(Member m1, Member m2) {
return m1.getName().compareTo(m2.getName());
}
});
}
XmlType xmlType = cls.getAnnotation(XmlType.class);
if (xmlType != null && xmlType.propOrder().length > 1 && !xmlType.propOrder()[0].isEmpty()) {
final List<String> orderList = Arrays.asList(xmlType.propOrder());
Collections.sort(combinedMembers, new Comparator<Member>() {
public int compare(Member m1, Member m2) {
String m1Name = getName(m1);
String m2Name = getName(m2);
int m1Index = orderList.indexOf(m1Name);
int m2Index = orderList.indexOf(m2Name);
if (m1Index != -1 && m2Index != -1) {
return m1Index - m2Index;
}
if (m1Index == -1 && m2Index != -1) {
return 1;
}
if (m1Index != -1 && m2Index == -1) {
return -1;
}
return 0;
}
});
}
for (Member member : combinedMembers) {
if (member instanceof Field) {
Field f = (Field)member;
QName fname = new QName(namespace, f.getName());
ReflectionUtil.setAccessible(f);
if (JAXBSchemaInitializer.isArray(f.getGenericType())) {
writeArrayObject(marshaller, writer, fname, f.get(elValue));
} else {
Object o = Utils.getFieldValue(f, elValue);
writeObject(marshaller, writer, newJAXBElement(fname, String.class, o));
}
} else { // it's a Method
Method m = (Method)member;
int idx = m.getName().startsWith("get") ? 3 : 2;
String name = m.getName().substring(idx);
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
QName mname = new QName(namespace, name);
if (JAXBSchemaInitializer.isArray(m.getGenericReturnType())) {
writeArrayObject(marshaller, writer, mname, m.invoke(elValue));
} else {
Object o = Utils.getMethodValue(m, elValue);
writeObject(marshaller, writer, newJAXBElement(mname, String.class, o));
}
}
}
writer.writeEndElement();
writer.flush();
} catch (Exception e) {
throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
} finally {
StaxUtils.close(writer);
}
}
private static String getName(Member m1) {
final String m1Name;
if (m1 instanceof Field) {
m1Name = ((Field)m1).getName();
} else {
int idx = m1.getName().startsWith("get") ? 3 : 2;
String name = m1.getName().substring(idx);
m1Name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
return m1Name;
}
private static void writeArrayObject(Marshaller marshaller,
Object source,
QName mname,
Object mObj) throws Fault, JAXBException {
// Have to handle this ourselves.... which really
// sucks.... but what can we do?
if (mObj == null) {
return;
}
Object objArray;
final Class<?> cls;
if (mObj instanceof List) {
List<?> l = (List<?>)mObj;
objArray = l.toArray();
cls = null;
} else {
objArray = mObj;
cls = objArray.getClass().getComponentType();
}
int len = Array.getLength(objArray);
for (int x = 0; x < len; x++) {
Object o = Array.get(objArray, x);
writeObject(marshaller, source,
newJAXBElement(mname, cls == null ? o.getClass() : cls, o));
}
}
public static Exception unmarshallException(Unmarshaller u,
Object source,
MessagePartInfo part) {
XMLStreamReader reader;
if (source instanceof XMLStreamReader) {
reader = (XMLStreamReader)source;
} else if (source instanceof Element) {
reader = StaxUtils.createXMLStreamReader((Element)source);
try {
// advance into the node
reader.nextTag();
} catch (XMLStreamException e) {
// ignore
}
} else {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
try {
QName qn = part.getElementQName();
if (!qn.equals(reader.getName())) {
throw new Fault(new Message("ELEMENT_NAME_MISMATCH", LOG, qn, reader.getName()));
}
Class<?> cls = part.getTypeClass();
Object obj;
try {
Constructor<?> cons = cls.getConstructor();
obj = cons.newInstance();
} catch (NoSuchMethodException nse) {
Constructor<?> cons = cls.getConstructor(new Class[] {String.class});
obj = cons.newInstance(new Object[1]);
}
XmlAccessType accessType = Utils.getXmlAccessType(cls);
reader.nextTag();
while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
QName q = reader.getName();
String fieldName = q.getLocalPart();
Field f = Utils.getField(cls, accessType, fieldName);
if (f != null) {
Type type = f.getGenericType();
ReflectionUtil.setAccessible(f);
if (JAXBSchemaInitializer.isArray(type)) {
Class<?> compType = JAXBSchemaInitializer.getArrayComponentType(type);
List<Object> ret = unmarshallArray(u, reader, q, compType, createList(type));
Object o = ret;
if (!isList(type)) {
if (compType.isPrimitive()) {
o = java.lang.reflect.Array.newInstance(compType, ret.size());
for (int x = 0; x < ret.size(); x++) {
Array.set(o, x, ret.get(x));
}
} else {
o = ret.toArray((Object[]) Array.newInstance(compType, ret.size()));
}
}
f.set(obj, o);
} else {
Object o = getElementValue(u.unmarshal(reader, Utils.getFieldType(f)));
Utils.setFieldValue(f, obj, o);
}
} else {
String s = StringUtils.capitalize(q.getLocalPart());
Method m = Utils.getMethod(cls, accessType, "get" + s);
if (m == null) {
m = Utils.getMethod(cls, accessType, "is" + s);
}
Type type = m.getGenericReturnType();
Object o;
if (JAXBSchemaInitializer.isArray(type)) {
Class<?> compType = JAXBSchemaInitializer
.getArrayComponentType(type);
List<Object> ret = unmarshallArray(u, reader,
q,
compType,
createList(type));
o = ret;
if (!isList(type)) {
if (compType.isPrimitive()) {
o = java.lang.reflect.Array.newInstance(compType, ret.size());
for (int x = 0; x < ret.size(); x++) {
Array.set(o, x, ret.get(x));
}
} else {
o = ret.toArray((Object[])Array.newInstance(compType, ret.size()));
}
}
} else {
o = getElementValue(u.unmarshal(reader, Utils.getMethodReturnType(m)));
}
Method m2 = Utils.getMethod(cls, accessType, "set" + s, m.getReturnType());
if (m2 != null) {
if (JAXBSchemaInitializer.isArray(type)) {
m2.invoke(obj, o);
} else {
Utils.setMethodValue(m, m2, obj, o);
}
} else {
Field fn = ReflectionUtil.getDeclaredField(cls, q.getLocalPart());
if (fn != null) {
ReflectionUtil.setAccessible(fn);
fn.set(obj, o);
}
}
}
if (reader.getEventType() == XMLStreamConstants.END_ELEMENT && q.equals(reader.getName())) {
reader.next();
}
}
return (Exception)obj;
} catch (Exception e) {
throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
}
}
private static void writeObject(Marshaller u, Object source, Object mObj) throws Fault, JAXBException {
if (source instanceof XMLStreamWriter) {
// allows the XML Stream Writer to adjust it's behaviour based on the state of the unmarshaller
if (source instanceof MarshallerAwareXMLWriter) {
((MarshallerAwareXMLWriter) source).setMarshaller(u);
}
u.marshal(mObj, (XMLStreamWriter)source);
} else if (source instanceof OutputStream) {
u.marshal(mObj, (OutputStream)source);
} else if (source instanceof Node) {
u.marshal(mObj, (Node)source);
} else if (source instanceof XMLEventWriter) {
// allows the XML Event Writer to adjust it's behaviour based on the state of the unmarshaller
if (source instanceof MarshallerAwareXMLWriter) {
((MarshallerAwareXMLWriter) source).setMarshaller(u);
}
u.marshal(mObj, (XMLEventWriter)source);
} else {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
}
private static XMLStreamWriter getStreamWriter(Object source) throws Fault {
if (source instanceof XMLStreamWriter) {
return (XMLStreamWriter)source;
} else if (source instanceof OutputStream) {
return StaxUtils.createXMLStreamWriter((OutputStream)source);
} else if (source instanceof Node) {
return new W3CDOMStreamWriter((Element)source);
}
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
public static void marshallNullElement(Marshaller marshaller,
Object source,
MessagePartInfo part) {
Class<?> clazz = part != null ? part.getTypeClass() : null;
try {
writeObject(marshaller, source, newJAXBElement(part.getElementQName(), clazz, null));
} catch (JAXBException e) {
throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
}
}
public static Object unmarshall(Unmarshaller u,
Object source,
MessagePartInfo part,
boolean unwrap) {
Class<?> clazz = part != null ? part.getTypeClass() : null;
if (clazz != null && Exception.class.isAssignableFrom(clazz)
&& Boolean.TRUE.equals(part.getProperty(JAXBDataBinding.class.getName() + ".CUSTOM_EXCEPTION"))) {
return unmarshallException(u, source, part);
}
QName elName = part != null ? part.getConcreteName() : null;
if (clazz != null && clazz.isArray()
&& part.getXmlSchema() instanceof XmlSchemaElement) {
XmlSchemaElement el = (XmlSchemaElement)part.getXmlSchema();
if (el.getSchemaType() instanceof XmlSchemaSimpleType
&& ((XmlSchemaSimpleType)el.getSchemaType()).getContent()
instanceof XmlSchemaSimpleTypeList) {
Object obj = unmarshall(u, source, elName, null, unwrap);
if (clazz.isArray() && obj instanceof List) {
return ((List<?>)obj).toArray((Object[])Array.newInstance(clazz.getComponentType(),
((List<?>)obj).size()));
}
return obj;
} else if (part.getMessageInfo().getOperation().isUnwrapped() && el.getMaxOccurs() != 1) {
// must read ourselves....
List<Object> ret = unmarshallArray(u, source, elName, clazz.getComponentType(),
createList(part));
Object o = ret;
if (!isList(part)) {
if (isSet(part)) {
o = createSet(part, ret);
} else if (clazz.getComponentType().isPrimitive()) {
o = java.lang.reflect.Array.newInstance(clazz.getComponentType(), ret.size());
for (int x = 0; x < ret.size(); x++) {
Array.set(o, x, ret.get(x));
}
} else {
o = ret.toArray((Object[])Array.newInstance(clazz.getComponentType(), ret.size()));
}
}
return o;
}
} else if (byte[].class == clazz && part.getTypeQName() != null
&& "hexBinary".equals(part.getTypeQName().getLocalPart())) {
String obj = (String)unmarshall(u, source, elName, String.class, unwrap);
return new HexBinaryAdapter().unmarshal(obj);
} else if (part != null && u.getSchema() != null
&& !(part.getXmlSchema() instanceof XmlSchemaElement)) {
//Validating RPC/Lit, make sure we don't try a root element name thing
source = updateSourceWithXSIType(source, part.getTypeQName());
}
Object o = unmarshall(u, source, elName, clazz, unwrap);
if (o != null && o.getClass().isArray() && isList(part)) {
List<Object> ret = createList(part);
Collections.addAll(ret, (Object[])o);
o = ret;
}
return o;
}
private static Object updateSourceWithXSIType(Object source, final QName typeQName) {
if (source instanceof XMLStreamReader
&& typeQName != null) {
XMLStreamReader reader = (XMLStreamReader)source;
String type = reader.getAttributeValue(Constants.URI_2001_SCHEMA_XSI, "type");
if (StringUtils.isEmpty(type)) {
source = new AddXSITypeStreamReader(reader, typeQName);
}
}
return source;
}
private static Object createSet(MessagePartInfo part, List<Object> ret) {
Type genericType = (Type)part.getProperty("generic.type");
Class<?> tp2 = (Class<?>)((ParameterizedType)genericType).getRawType();
if (tp2.isInterface()) {
return new HashSet<>(ret);
}
Collection<Object> c;
try {
c = CastUtils.cast((Collection<?>)tp2.newInstance());
} catch (Exception e) {
c = new HashSet<>();
}
c.addAll(ret);
return c;
}
private static boolean isSet(MessagePartInfo part) {
if (part.getTypeClass().isArray() && !part.getTypeClass().getComponentType().isPrimitive()) {
// && Collection.class.isAssignableFrom(part.getTypeClass())) {
// it's List Para
//
Type genericType = (Type)part.getProperty("generic.type");
if (genericType instanceof ParameterizedType) {
Type tp2 = ((ParameterizedType)genericType).getRawType();
if (tp2 instanceof Class) {
return Set.class.isAssignableFrom((Class<?>)tp2);
}
}
}
return false;
}
private static List<Object> createList(MessagePartInfo part) {
Type genericType = (Type)part.getProperty("generic.type");
return createList(genericType);
}
private static List<Object> createList(Type genericType) {
if (genericType instanceof ParameterizedType) {
Type tp2 = ((ParameterizedType)genericType).getRawType();
if (tp2 instanceof Class) {
Class<?> cls = (Class<?>)tp2;
if (!cls.isInterface() && List.class.isAssignableFrom(cls)) {
try {
return CastUtils.cast((List<?>)cls.newInstance());
} catch (Exception e) {
// ignore, just return an ArrayList
}
}
}
}
return new ArrayList<>();
}
private static boolean isList(Type cls) {
return cls instanceof ParameterizedType;
}
private static boolean isList(MessagePartInfo part) {
if (part.getTypeClass().isArray() && !part.getTypeClass().getComponentType().isPrimitive()) {
// && Collection.class.isAssignableFrom(part.getTypeClass())) {
// it's List Para
//
Type genericType = (Type)part.getProperty("generic.type");
if (genericType instanceof ParameterizedType) {
Type tp2 = ((ParameterizedType)genericType).getRawType();
if (tp2 instanceof Class) {
return List.class.isAssignableFrom((Class<?>)tp2);
}
}
}
return false;
}
private static Object doUnmarshal(final Unmarshaller u,
final Object source,
final QName elName,
final Class<?> clazz,
final boolean unwrap) throws Exception {
final Object obj;
boolean unmarshalWithClass = true;
if (clazz == null
|| (!clazz.isPrimitive()
&& !clazz.isArray()
&& !clazz.isEnum()
&& !clazz.equals(Calendar.class)
&& (Modifier.isAbstract(clazz.getModifiers())
|| Modifier.isInterface(clazz.getModifiers())))) {
unmarshalWithClass = false;
}
if (clazz != null
&& ("javax.xml.datatype.XMLGregorianCalendar".equals(clazz.getName())
|| "javax.xml.datatype.Duration".equals(clazz.getName()))) {
// special treat two jaxb defined built-in abstract types
unmarshalWithClass = true;
}
if (source instanceof Node) {
obj = unmarshalWithClass ? u.unmarshal((Node)source, clazz)
: u.unmarshal((Node)source);
} else if (source instanceof DepthXMLStreamReader) {
// JAXB optimizes a ton of stuff depending on the StreamReader impl. Thus,
// we REALLY want to pass the original reader in. This is OK with JAXB
// as it doesn't read beyond the end so the DepthXMLStreamReader state
// would be OK when it returns. The main winner is FastInfoset where parsing
// a testcase I have goes from about 300/sec to well over 1000.
DepthXMLStreamReader dr = (DepthXMLStreamReader)source;
XMLStreamReader reader = dr.getReader();
// allows the XML Stream Reader to adjust it's behaviour based on the state of the unmarshaller
if (reader instanceof UnmarshallerAwareXMLReader) {
((UnmarshallerAwareXMLReader) reader).setUnmarshaller(u);
}
if (u.getSchema() != null) {
//validating, but we may need more namespaces
reader = findExtraNamespaces(reader);
}
obj = unmarshalWithClass ? u.unmarshal(reader, clazz) : u
.unmarshal(dr.getReader());
} else if (source instanceof XMLStreamReader) {
XMLStreamReader reader = (XMLStreamReader)source;
// allows the XML Stream Reader to adjust it's behaviour based on the state of the unmarshaller
if (reader instanceof UnmarshallerAwareXMLReader) {
((UnmarshallerAwareXMLReader) reader).setUnmarshaller(u);
}
if (u.getSchema() != null) {
//validating, but we may need more namespaces
reader = findExtraNamespaces(reader);
}
obj = unmarshalWithClass ? u.unmarshal(reader, clazz) : u
.unmarshal(reader);
} else if (source instanceof XMLEventReader) {
// allows the XML Event Reader to adjust it's behaviour based on the state of the unmarshaller
if (source instanceof UnmarshallerAwareXMLReader) {
((UnmarshallerAwareXMLReader) source).setUnmarshaller(u);
}
obj = unmarshalWithClass ? u.unmarshal((XMLEventReader)source, clazz) : u
.unmarshal((XMLEventReader)source);
} else if (source == null) {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, "null"));
} else {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
return unwrap ? getElementValue(obj) : obj;
}
public static Object unmarshall(final Unmarshaller u,
final Object source,
final QName elName,
final Class<?> clazz,
final boolean unwrap) {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
return doUnmarshal(u, source, elName, clazz, unwrap);
}
});
} catch (PrivilegedActionException e) {
Exception ex = e.getException();
if (ex instanceof Fault) {
throw (Fault)ex;
}
if (ex instanceof javax.xml.bind.UnmarshalException) {
javax.xml.bind.UnmarshalException unmarshalEx = (javax.xml.bind.UnmarshalException)ex;
if (unmarshalEx.getLinkedException() != null) {
throw new Fault(new Message("UNMARSHAL_ERROR", LOG,
unmarshalEx.getLinkedException().getMessage()), ex);
}
throw new Fault(new Message("UNMARSHAL_ERROR", LOG,
unmarshalEx.getMessage()), ex);
}
throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getMessage()), ex);
}
}
private static XMLStreamReader findExtraNamespaces(XMLStreamReader source) {
//due to a deficiency in the Stax API, there isn't a way to get all
//the namespace prefixes that are "valid" at this point. Thus, JAXB
//cannot set all the prefixes into the validator (which also doesn't allow
//setting a NSContext, just allows declaring of prefixes) so resolving
//prefixes and such will fail if they were declared on any of the parent
//elements.
//
//We'll use some reflection to grab the known namespaces from woodstox
//or the xerces parser and fake extra namespace decls on the root elements.
//slight performance penalty, but there already is a penalty if you are validating
//anyway.
NamespaceContext c = source.getNamespaceContext();
final Map<String, String> nsMap = new TreeMap<>();
try {
if (c instanceof W3CNamespaceContext) {
Element element = ((W3CNamespaceContext)c).getElement();
while (element != null) {
NamedNodeMap namedNodeMap = element.getAttributes();
for (int i = 0; i < namedNodeMap.getLength(); i++) {
Attr attr = (Attr)namedNodeMap.item(i);
if (attr.getPrefix() != null && "xmlns".equals(attr.getPrefix())) {
nsMap.put(attr.getLocalName(), attr.getValue());
}
}
element = (Element)element.getParentNode();
}
} else {
try {
//Woodstox version
c = (NamespaceContext)c.getClass().getMethod("createNonTransientNsContext",
Location.class)
.invoke(c, new Object[1]);
} catch (Throwable t) {
//ignore
}
Field f = ReflectionUtil.getDeclaredField(c.getClass(), "mNamespaces");
ReflectionUtil.setAccessible(f);
String[] ns = (String[])f.get(c);
for (int x = 0; x < ns.length; x += 2) {
if (ns[x] == null) {
nsMap.put("", ns[x + 1]);
} else {
nsMap.put(ns[x], ns[x + 1]);
}
}
}
} catch (Throwable t) {
//internal JDK/xerces version
try {
Field f = ReflectionUtil.getDeclaredField(c.getClass(), "fNamespaceContext");
ReflectionUtil.setAccessible(f);
Object c2 = f.get(c);
Enumeration<?> enm = (Enumeration<?>)c2.getClass().getMethod("getAllPrefixes").invoke(c2);
while (enm.hasMoreElements()) {
String s = (String)enm.nextElement();
if (s == null) {
nsMap.put("", c.getNamespaceURI(null));
} else {
nsMap.put(s, c.getNamespaceURI(s));
}
}
} catch (Throwable t2) {
//ignore
}
}
if (!nsMap.isEmpty()) {
for (int x = 0; x < source.getNamespaceCount(); x++) {
String pfx = source.getNamespacePrefix(x);
if (pfx == null) {
nsMap.remove("");
} else {
nsMap.remove(pfx);
}
}
if (!nsMap.isEmpty()) {
@SuppressWarnings("unchecked")
final Map.Entry<String, String>[] namespaces
= nsMap.entrySet().toArray(new Map.Entry[nsMap.size()]);
//OK. we have extra namespaces. We'll need to wrapper the reader
//with a new one that will fake extra namespace events
source = new DepthXMLStreamReader(source) {
public int getNamespaceCount() {
if (getDepth() == 0 && isStartElement()) {
return super.getNamespaceCount() + nsMap.size();
}
return super.getNamespaceCount();
}
public String getNamespacePrefix(int arg0) {
if (getDepth() == 0 && isStartElement()) {
int i = super.getNamespaceCount();
if (arg0 >= i) {
arg0 -= i;
return namespaces[arg0].getKey();
}
}
return super.getNamespacePrefix(arg0);
}
public String getNamespaceURI(int arg0) {
if (getDepth() == 0 && isStartElement()) {
int i = super.getNamespaceCount();
if (arg0 >= i) {
arg0 -= i;
return namespaces[arg0].getValue();
}
}
return super.getNamespaceURI(arg0);
}
};
}
}
return source;
}
public static Object getElementValue(Object obj) {
if (null == obj) {
return null;
}
if (obj instanceof JAXBElement) {
return ((JAXBElement<?>)obj).getValue();
}
return obj;
}
public static Class<?> getClassFromType(Type t) {
if (t instanceof Class) {
return (Class<?>)t;
} else if (t instanceof GenericArrayType) {
GenericArrayType g = (GenericArrayType)t;
return Array.newInstance(getClassFromType(g.getGenericComponentType()), 0).getClass();
} else if (t instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType)t;
return getClassFromType(p.getRawType());
}
// TypeVariable and WildCardType are not handled as it is unlikely such
// Types will
// JAXB Code Generated.
assert false;
throw new IllegalArgumentException("Cannot get Class object from unknown Type");
}
public static List<Object> unmarshallArray(Unmarshaller u, Object source,
QName elName, Class<?> clazz,
List<Object> ret) {
try {
XMLStreamReader reader;
if (source instanceof XMLStreamReader) {
reader = (XMLStreamReader)source;
} else if (source instanceof Element) {
reader = StaxUtils.createXMLStreamReader((Element)source);
} else {
throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
}
while (reader.getName().equals(elName)) {
JAXBElement<?> type = u.unmarshal(reader, clazz);
if (type != null) {
ret.add(type.getValue());
}
while (reader.getEventType() != XMLStreamConstants.START_ELEMENT
&& reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
reader.nextTag();
}
}
return ret;
} catch (Fault ex) {
throw ex;
} catch (javax.xml.bind.MarshalException ex) {
throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getLinkedException()
.getMessage()), ex);
} catch (Exception ex) {
throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getMessage()), ex);
}
}
}