| /** |
| * 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.IOException; |
| import java.io.OutputStream; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.JAXBElement; |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.Marshaller; |
| import javax.xml.bind.Unmarshaller; |
| import javax.xml.bind.ValidationEventHandler; |
| import javax.xml.bind.annotation.XmlElement; |
| import javax.xml.bind.annotation.XmlElementRef; |
| import javax.xml.bind.annotation.adapters.XmlAdapter; |
| import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.XMLEventReader; |
| import javax.xml.stream.XMLEventWriter; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.transform.dom.DOMResult; |
| import javax.xml.transform.dom.DOMSource; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| |
| import org.xml.sax.InputSource; |
| |
| import org.apache.cxf.Bus; |
| import org.apache.cxf.common.injection.NoJSR250Annotations; |
| import org.apache.cxf.common.jaxb.JAXBBeanInfo; |
| import org.apache.cxf.common.jaxb.JAXBContextCache; |
| import org.apache.cxf.common.jaxb.JAXBContextCache.CachedContextAndSchemas; |
| import org.apache.cxf.common.jaxb.JAXBContextProxy; |
| import org.apache.cxf.common.jaxb.JAXBUtils; |
| import org.apache.cxf.common.logging.LogUtils; |
| import org.apache.cxf.common.util.PackageUtils; |
| import org.apache.cxf.common.util.PropertyUtils; |
| import org.apache.cxf.common.util.ReflectionUtil; |
| import org.apache.cxf.common.xmlschema.SchemaCollection; |
| import org.apache.cxf.databinding.AbstractInterceptorProvidingDataBinding; |
| import org.apache.cxf.databinding.AbstractWrapperHelper; |
| import org.apache.cxf.databinding.DataReader; |
| import org.apache.cxf.databinding.DataWriter; |
| import org.apache.cxf.databinding.WrapperCapableDatabinding; |
| import org.apache.cxf.databinding.WrapperHelper; |
| import org.apache.cxf.interceptor.InterceptorProvider; |
| import org.apache.cxf.jaxb.attachment.JAXBAttachmentSchemaValidationHack; |
| import org.apache.cxf.jaxb.io.DataReaderImpl; |
| import org.apache.cxf.jaxb.io.DataWriterImpl; |
| import org.apache.cxf.resource.URIResolver; |
| import org.apache.cxf.service.Service; |
| import org.apache.cxf.service.factory.ServiceConstructionException; |
| import org.apache.cxf.service.model.MessageInfo; |
| import org.apache.cxf.service.model.MessagePartInfo; |
| import org.apache.cxf.service.model.ServiceInfo; |
| import org.apache.cxf.staxutils.StaxUtils; |
| import org.apache.cxf.ws.addressing.ObjectFactory; |
| |
| @NoJSR250Annotations |
| public class JAXBDataBinding extends AbstractInterceptorProvidingDataBinding |
| implements WrapperCapableDatabinding, InterceptorProvider { |
| |
| public static final String READER_VALIDATION_EVENT_HANDLER = "jaxb-reader-validation-event-handler"; |
| public static final String VALIDATION_EVENT_HANDLER = "jaxb-validation-event-handler"; |
| public static final String SET_VALIDATION_EVENT_HANDLER = "set-jaxb-validation-event-handler"; |
| public static final String WRITER_VALIDATION_EVENT_HANDLER = "jaxb-writer-validation-event-handler"; |
| |
| public static final String SCHEMA_RESOURCE = "SCHEMRESOURCE"; |
| public static final String MTOM_THRESHOLD = "org.apache.cxf.jaxb.mtomThreshold"; |
| |
| public static final String UNWRAP_JAXB_ELEMENT = "unwrap.jaxb.element"; |
| |
| public static final String USE_JAXB_BRIDGE = "use.jaxb.bridge"; |
| |
| public static final String JAXB_SCAN_PACKAGES = "jaxb.scanPackages"; |
| |
| private static final Logger LOG = LogUtils.getLogger(JAXBDataBinding.class); |
| |
| private static final Class<?>[] SUPPORTED_READER_FORMATS = new Class<?>[] {Node.class, |
| XMLEventReader.class, |
| XMLStreamReader.class}; |
| private static final Class<?>[] SUPPORTED_WRITER_FORMATS = new Class<?>[] {OutputStream.class, |
| Node.class, |
| XMLEventWriter.class, |
| XMLStreamWriter.class}; |
| |
| private static class DelayedDOMResult extends DOMResult { |
| private final URL resource; |
| private final String publicId; |
| DelayedDOMResult(URL url, String sysId, String pId) { |
| super(null, sysId); |
| resource = url; |
| publicId = pId; |
| } |
| public synchronized Node getNode() { |
| Node nd = super.getNode(); |
| if (nd == null) { |
| try { |
| InputSource src = new InputSource(resource.openStream()); |
| src.setSystemId(this.getSystemId()); |
| src.setPublicId(publicId); |
| Document doc = StaxUtils.read(src); |
| setNode(doc); |
| nd = super.getNode(); |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| return nd; |
| } |
| } |
| private static final Map<String, DOMResult> BUILT_IN_SCHEMAS = new HashMap<>(); |
| static { |
| try (URIResolver resolver = new URIResolver()) { |
| try { |
| resolver.resolve("", "classpath:/schemas/wsdl/ws-addr-wsdl.xsd", JAXBDataBinding.class); |
| if (resolver.isResolved()) { |
| resolver.getInputStream().close(); |
| DOMResult dr = new DelayedDOMResult(resolver.getURL(), |
| "classpath:/schemas/wsdl/ws-addr-wsdl.xsd", |
| "http://www.w3.org/2005/02/addressing/wsdl"); |
| BUILT_IN_SCHEMAS.put("http://www.w3.org/2005/02/addressing/wsdl", dr); |
| resolver.unresolve(); |
| } |
| } catch (Exception e) { |
| //IGNORE |
| } |
| try { |
| resolver.resolve("", "classpath:/schemas/wsdl/ws-addr.xsd", JAXBDataBinding.class); |
| if (resolver.isResolved()) { |
| resolver.getInputStream().close(); |
| DOMResult dr = new DelayedDOMResult(resolver.getURL(), |
| "classpath:/schemas/wsdl/ws-addr.xsd", |
| "http://www.w3.org/2005/08/addressing"); |
| BUILT_IN_SCHEMAS.put("http://www.w3.org/2005/08/addressing", dr); |
| resolver.unresolve(); |
| } |
| } catch (Exception e) { |
| //IGNORE |
| } |
| try { |
| resolver.resolve("", "classpath:/schemas/wsdl/wsrm.xsd", JAXBDataBinding.class); |
| if (resolver.isResolved()) { |
| resolver.getInputStream().close(); |
| DOMResult dr = new DelayedDOMResult(resolver.getURL(), |
| "classpath:/schemas/wsdl/wsrm.xsd", |
| "http://schemas.xmlsoap.org/ws/2005/02/rm"); |
| BUILT_IN_SCHEMAS.put("http://schemas.xmlsoap.org/ws/2005/02/rm", dr); |
| resolver.unresolve(); |
| } |
| } catch (Exception e) { |
| //IGNORE |
| } |
| } catch (Exception e) { |
| //IGNORE |
| } |
| } |
| |
| Class<?>[] extraClass; |
| |
| JAXBContext context; |
| Set<Class<?>> contextClasses; |
| Collection<Object> typeRefs = new ArrayList<>(); |
| |
| Class<?> cls; |
| |
| private Map<String, Object> contextProperties = new HashMap<>(); |
| private List<XmlAdapter<?, ?>> adapters = new ArrayList<>(); |
| private Map<String, Object> marshallerProperties = new HashMap<>(); |
| private Map<String, Object> unmarshallerProperties = new HashMap<>(); |
| private Unmarshaller.Listener unmarshallerListener; |
| private Marshaller.Listener marshallerListener; |
| private ValidationEventHandler validationEventHandler; |
| private Object escapeHandler; |
| private Object noEscapeHandler; |
| |
| private boolean unwrapJAXBElement = true; |
| private boolean scanPackages = true; |
| private boolean qualifiedSchemas; |
| |
| public JAXBDataBinding() { |
| } |
| |
| public JAXBDataBinding(boolean q) { |
| this.qualifiedSchemas = q; |
| } |
| |
| public JAXBDataBinding(Class<?>... classes) throws JAXBException { |
| contextClasses = new LinkedHashSet<>(Arrays.asList(classes)); |
| setContext(createJAXBContext(contextClasses)); //NOPMD - specifically allow this |
| } |
| public JAXBDataBinding(boolean qualified, Map<String, Object> props) throws JAXBException { |
| this(qualified); |
| if (props != null && props.get("jaxb.additionalContextClasses") != null) { |
| Object o = props.get("jaxb.additionalContextClasses"); |
| if (o instanceof Class) { |
| o = new Class[] {(Class<?>)o}; |
| } |
| extraClass = (Class[])o; |
| } |
| |
| // the default for scan packages is true, so the jaxb scan packages |
| // property must be explicitly set to false to disable it |
| if (PropertyUtils.isFalse(props, JAXB_SCAN_PACKAGES)) { |
| scanPackages = false; |
| } |
| } |
| |
| public JAXBDataBinding(JAXBContext context) { |
| this(); |
| setContext(context); |
| } |
| |
| protected boolean getQualifiedSchemas() { |
| return qualifiedSchemas; |
| } |
| |
| public JAXBContext getContext() { |
| return context; |
| } |
| |
| public final void setContext(JAXBContext ctx) { |
| context = ctx; |
| //create default MininumEscapeHandler |
| escapeHandler = JAXBUtils.createMininumEscapeHandler(ctx.getClass()); |
| noEscapeHandler = JAXBUtils.createNoEscapeHandler(ctx.getClass()); |
| } |
| |
| public Object getEscapeHandler() { |
| return escapeHandler; |
| } |
| |
| public void setEscapeHandler(Object handler) { |
| escapeHandler = handler; |
| } |
| |
| public void applyEscapeHandler(boolean escape, Consumer<Object> consumer) { |
| if (escape) { |
| consumer.accept(escapeHandler); |
| } else if (noEscapeHandler != null) { |
| consumer.accept(noEscapeHandler); |
| } |
| } |
| |
| |
| @SuppressWarnings("unchecked") |
| public <T> DataWriter<T> createWriter(Class<T> c) { |
| |
| Integer mtomThresholdInt = Integer.valueOf(getMtomThreshold()); |
| if (c == XMLStreamWriter.class) { |
| DataWriterImpl<XMLStreamWriter> r |
| = new DataWriterImpl<>(getBus(), this, true); |
| r.setMtomThreshold(mtomThresholdInt); |
| return (DataWriter<T>)r; |
| } else if (c == OutputStream.class) { |
| DataWriterImpl<OutputStream> r = new DataWriterImpl<>(getBus(), this, false); |
| r.setMtomThreshold(mtomThresholdInt); |
| return (DataWriter<T>)r; |
| } else if (c == XMLEventWriter.class) { |
| DataWriterImpl<XMLEventWriter> r = new DataWriterImpl<>(getBus(), this, true); |
| r.setMtomThreshold(mtomThresholdInt); |
| return (DataWriter<T>)r; |
| } else if (c == Node.class) { |
| DataWriterImpl<Node> r = new DataWriterImpl<>(getBus(), this, false); |
| r.setMtomThreshold(mtomThresholdInt); |
| return (DataWriter<T>)r; |
| } |
| return null; |
| } |
| |
| public Class<?>[] getSupportedWriterFormats() { |
| return SUPPORTED_WRITER_FORMATS; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public <T> DataReader<T> createReader(Class<T> c) { |
| DataReader<T> dr = null; |
| if (c == XMLStreamReader.class) { |
| dr = (DataReader<T>)new DataReaderImpl<XMLStreamReader>(this, unwrapJAXBElement); |
| } else if (c == XMLEventReader.class) { |
| dr = (DataReader<T>)new DataReaderImpl<XMLEventReader>(this, unwrapJAXBElement); |
| } else if (c == Node.class) { |
| dr = (DataReader<T>)new DataReaderImpl<Node>(this, unwrapJAXBElement); |
| } |
| |
| return dr; |
| } |
| |
| public Class<?>[] getSupportedReaderFormats() { |
| return SUPPORTED_READER_FORMATS; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public synchronized void initialize(Service service) { |
| |
| inInterceptors.addIfAbsent(JAXBAttachmentSchemaValidationHack.INSTANCE); |
| inFaultInterceptors.addIfAbsent(JAXBAttachmentSchemaValidationHack.INSTANCE); |
| |
| // context is already set, don't redo it |
| if (context != null) { |
| return; |
| } |
| |
| contextClasses = new LinkedHashSet<>(); |
| |
| for (ServiceInfo serviceInfo : service.getServiceInfos()) { |
| JAXBContextInitializer initializer = new JAXBContextInitializer(getBus(), serviceInfo, contextClasses, |
| typeRefs, this.getUnmarshallerProperties()); |
| initializer.walk(); |
| if (serviceInfo.getProperty("extra.class") != null) { |
| Set<Class<?>> exClasses = serviceInfo.getProperty("extra.class", Set.class); |
| contextClasses.addAll(exClasses); |
| } |
| |
| } |
| |
| String tns = getNamespaceToUse(service); |
| final CachedContextAndSchemas cachedContextAndSchemas; |
| try { |
| cachedContextAndSchemas = createJAXBContextAndSchemas(contextClasses, tns); |
| } catch (JAXBException e1) { |
| throw new ServiceConstructionException(e1); |
| } |
| final JAXBContext ctx = cachedContextAndSchemas.getContext(); |
| if (LOG.isLoggable(Level.FINE)) { |
| LOG.log(Level.FINE, "CREATED_JAXB_CONTEXT", new Object[] {ctx, contextClasses}); |
| } |
| setContext(ctx); |
| |
| for (ServiceInfo serviceInfo : service.getServiceInfos()) { |
| SchemaCollection col = serviceInfo.getXmlSchemaCollection(); |
| |
| if (col.getXmlSchemas().length > 1) { |
| // someone has already filled in the types |
| justCheckForJAXBAnnotations(serviceInfo); |
| continue; |
| } |
| |
| boolean schemasFromCache = false; |
| Collection<DOMSource> schemas = getSchemas(); |
| if (schemas == null || schemas.isEmpty()) { |
| schemas = cachedContextAndSchemas.getSchemas(); |
| if (schemas != null) { |
| schemasFromCache = true; |
| } |
| } else { |
| schemasFromCache = true; |
| } |
| Set<DOMSource> bi = new LinkedHashSet<>(); |
| if (schemas == null) { |
| schemas = new LinkedHashSet<>(); |
| try { |
| for (DOMResult r : generateJaxbSchemas()) { |
| DOMSource src = new DOMSource(r.getNode(), r.getSystemId()); |
| if (isInBuiltInSchemas(r)) { |
| bi.add(src); |
| } else { |
| schemas.add(src); |
| } |
| } |
| //put any builtins at the end. Anything that DOES import them |
| //will cause it to load automatically and we'll skip them later |
| schemas.addAll(bi); |
| } catch (IOException e) { |
| throw new ServiceConstructionException("SCHEMA_GEN_EXC", LOG, e); |
| } |
| } |
| for (DOMSource r : schemas) { |
| if (bi.contains(r)) { |
| String ns = ((Document)r.getNode()).getDocumentElement().getAttribute("targetNamespace"); |
| if (serviceInfo.getSchema(ns) != null) { |
| continue; |
| } |
| } |
| //StaxUtils.print(r.getNode()); |
| //System.out.println(); |
| addSchemaDocument(serviceInfo, |
| col, |
| (Document)r.getNode(), |
| r.getSystemId()); |
| } |
| |
| JAXBSchemaInitializer schemaInit = new JAXBSchemaInitializer(serviceInfo, col, context, |
| this.qualifiedSchemas, tns); |
| schemaInit.walk(); |
| if (cachedContextAndSchemas != null && !schemasFromCache) { |
| cachedContextAndSchemas.setSchemas(schemas); |
| } |
| } |
| } |
| |
| protected void justCheckForJAXBAnnotations(ServiceInfo serviceInfo) { |
| for (MessageInfo mi: serviceInfo.getMessages().values()) { |
| for (MessagePartInfo mpi : mi.getMessageParts()) { |
| checkForJAXBAnnotations(mpi, serviceInfo.getXmlSchemaCollection(), serviceInfo.getTargetNamespace()); |
| } |
| } |
| } |
| private void checkForJAXBAnnotations(MessagePartInfo mpi, SchemaCollection schemaCollection, String ns) { |
| Annotation[] anns = (Annotation[])mpi.getProperty("parameter.annotations"); |
| JAXBContextProxy ctx = JAXBUtils.createJAXBContextProxy(context, schemaCollection, ns); |
| XmlJavaTypeAdapter jta = JAXBSchemaInitializer.findFromTypeAdapter(ctx, mpi.getTypeClass(), anns); |
| if (jta != null) { |
| JAXBBeanInfo jtaBeanInfo = JAXBSchemaInitializer.findFromTypeAdapter(ctx, jta.value()); |
| JAXBBeanInfo beanInfo = JAXBSchemaInitializer.getBeanInfo(ctx, mpi.getTypeClass()); |
| if (jtaBeanInfo != beanInfo) { |
| mpi.setProperty("parameter.annotations", anns); |
| mpi.setProperty("honor.jaxb.annotations", Boolean.TRUE); |
| } |
| } |
| } |
| |
| protected String getNamespaceToUse(Service service) { |
| if ("true".equals(service.get("org.apache.cxf.databinding.namespace"))) { |
| return null; |
| } |
| final String tns; |
| if (!service.getServiceInfos().isEmpty()) { |
| tns = service.getServiceInfos().get(0).getInterface().getName().getNamespaceURI(); |
| } else { |
| tns = service.getName().getNamespaceURI(); |
| } |
| return tns; |
| } |
| |
| public void setExtraClass(Class<?>[] userExtraClass) { |
| extraClass = userExtraClass; |
| } |
| |
| public Class<?>[] getExtraClass() { |
| return extraClass; |
| } |
| |
| // default access for tests. |
| List<DOMResult> generateJaxbSchemas() throws IOException { |
| return JAXBUtils.generateJaxbSchemas(context, BUILT_IN_SCHEMAS); |
| } |
| |
| public JAXBContext createJAXBContext(Set<Class<?>> classes) throws JAXBException { |
| return createJAXBContext(classes, null); |
| } |
| |
| public JAXBContext createJAXBContext(Set<Class<?>> classes, String defaultNs) throws JAXBException { |
| return createJAXBContextAndSchemas(classes, defaultNs).getContext(); |
| } |
| |
| public CachedContextAndSchemas createJAXBContextAndSchemas(Set<Class<?>> classes, |
| String defaultNs) |
| throws JAXBException { |
| //add user extra class into jaxb context |
| if (extraClass != null && extraClass.length > 0) { |
| for (Class<?> clz : extraClass) { |
| classes.add(clz); |
| } |
| } |
| if (scanPackages) { |
| JAXBContextCache.scanPackages(classes); |
| } |
| addWsAddressingTypes(classes); |
| |
| return JAXBContextCache.getCachedContextAndSchemas(classes, defaultNs, |
| contextProperties, |
| typeRefs, true); |
| } |
| |
| |
| private void addWsAddressingTypes(Set<Class<?>> classes) { |
| if (classes.contains(ObjectFactory.class)) { |
| // ws-addressing is used, lets add the specific types |
| try { |
| classes.add(Class.forName("org.apache.cxf.ws.addressing.wsdl.ObjectFactory")); |
| classes.add(Class.forName("org.apache.cxf.ws.addressing.wsdl.AttributedQNameType")); |
| classes.add(Class.forName("org.apache.cxf.ws.addressing.wsdl.ServiceNameType")); |
| } catch (ClassNotFoundException unused) { |
| // REVISIT - ignorable if WS-ADDRESSING not available? |
| // maybe add a way to allow interceptors to add stuff to the |
| // context? |
| } |
| } |
| } |
| |
| public Set<Class<?>> getContextClasses() { |
| return Collections.unmodifiableSet(this.contextClasses); |
| } |
| |
| /** |
| * Return a map of properties. These properties are passed to |
| * JAXBContext.newInstance when this object creates a context. |
| * |
| * @return the map of JAXB context properties. |
| */ |
| public Map<String, Object> getContextProperties() { |
| return contextProperties; |
| } |
| |
| /** |
| * Set a map of JAXB context properties. These properties are passed to |
| * JAXBContext.newInstance when this object creates a context. Note that if |
| * you create a JAXB context elsewhere, you will not respect these |
| * properties unless you handle it manually. |
| * |
| * @param contextProperties map of properties. |
| */ |
| public void setContextProperties(Map<String, Object> contextProperties) { |
| this.contextProperties = contextProperties; |
| } |
| |
| public List<XmlAdapter<?, ?>> getConfiguredXmlAdapters() { |
| return adapters; |
| } |
| |
| public void setConfiguredXmlAdapters(List<XmlAdapter<?, ?>> adpters) { |
| this.adapters = adpters; |
| } |
| |
| /** |
| * Return a map of properties. These properties are set into the JAXB |
| * Marshaller (via Marshaller.setProperty(...) when the marshaller is |
| * created. |
| * |
| * @return the map of JAXB marshaller properties. |
| */ |
| public Map<String, Object> getMarshallerProperties() { |
| return marshallerProperties; |
| } |
| |
| /** |
| * Set a map of JAXB marshaller properties. These properties are set into |
| * the JAXB Marshaller (via Marshaller.setProperty(...) when the marshaller |
| * is created. |
| * |
| * @param marshallerProperties map of properties. |
| */ |
| public void setMarshallerProperties(Map<String, Object> marshallerProperties) { |
| this.marshallerProperties = marshallerProperties; |
| } |
| |
| |
| /** |
| * Return a map of properties. These properties are set into the JAXB |
| * Unmarshaller (via Unmarshaller.setProperty(...) when the unmarshaller is |
| * created. |
| * |
| * @return the map of JAXB unmarshaller properties. |
| */ |
| public Map<String, Object> getUnmarshallerProperties() { |
| return unmarshallerProperties; |
| } |
| |
| /** |
| * Set a map of JAXB unmarshaller properties. These properties are set into |
| * the JAXB Unmarshaller (via Unmarshaller.setProperty(...) when the unmarshaller |
| * is created. |
| * |
| * @param unmarshallerProperties map of properties. |
| */ |
| public void setUnmarshallerProperties(Map<String, Object> unmarshallerProperties) { |
| this.unmarshallerProperties = unmarshallerProperties; |
| } |
| |
| /** |
| * Returns the Unmarshaller.Listener that will be registered on the Unmarshallers |
| * @return |
| */ |
| public Unmarshaller.Listener getUnmarshallerListener() { |
| return unmarshallerListener; |
| } |
| |
| /** |
| * Sets the Unmarshaller.Listener that will be registered on the Unmarshallers |
| * @param unmarshallerListener |
| */ |
| public void setUnmarshallerListener(Unmarshaller.Listener unmarshallerListener) { |
| this.unmarshallerListener = unmarshallerListener; |
| } |
| /** |
| * Returns the Marshaller.Listener that will be registered on the Marshallers |
| * @return |
| */ |
| public Marshaller.Listener getMarshallerListener() { |
| return marshallerListener; |
| } |
| |
| /** |
| * Sets the Marshaller.Listener that will be registered on the Marshallers |
| * @param marshallerListener |
| */ |
| public void setMarshallerListener(Marshaller.Listener marshallerListener) { |
| this.marshallerListener = marshallerListener; |
| } |
| |
| |
| public ValidationEventHandler getValidationEventHandler() { |
| return validationEventHandler; |
| } |
| |
| public void setValidationEventHandler(ValidationEventHandler validationEventHandler) { |
| this.validationEventHandler = validationEventHandler; |
| } |
| |
| |
| public boolean isUnwrapJAXBElement() { |
| return unwrapJAXBElement; |
| } |
| |
| public void setUnwrapJAXBElement(boolean unwrapJAXBElement) { |
| this.unwrapJAXBElement = unwrapJAXBElement; |
| } |
| |
| public WrapperHelper createWrapperHelper(Class<?> wrapperType, QName wrapperName, List<String> partNames, |
| List<String> elTypeNames, List<Class<?>> partClasses) { |
| List<Method> getMethods = new ArrayList<>(partNames.size()); |
| List<Method> setMethods = new ArrayList<>(partNames.size()); |
| List<Method> jaxbMethods = new ArrayList<>(partNames.size()); |
| List<Field> fields = new ArrayList<>(partNames.size()); |
| |
| Method[] allMethods = wrapperType.getMethods(); |
| String packageName = PackageUtils.getPackageName(wrapperType); |
| |
| //if wrappertype class is generated by ASM, getPackage() always return null |
| if (wrapperType.getPackage() != null) { |
| packageName = wrapperType.getPackage().getName(); |
| } |
| |
| String objectFactoryClassName = packageName + ".ObjectFactory"; |
| |
| Object objectFactory = null; |
| try { |
| objectFactory = wrapperType.getClassLoader().loadClass(objectFactoryClassName).newInstance(); |
| } catch (Exception e) { |
| //ignore, probably won't need it |
| } |
| Method[] allOFMethods; |
| if (objectFactory != null) { |
| allOFMethods = objectFactory.getClass().getMethods(); |
| } else { |
| allOFMethods = new Method[0]; |
| } |
| |
| for (int x = 0; x < partNames.size(); x++) { |
| String partName = partNames.get(x); |
| if (partName == null) { |
| getMethods.add(null); |
| setMethods.add(null); |
| fields.add(null); |
| jaxbMethods.add(null); |
| continue; |
| } |
| |
| String elementType = elTypeNames.get(x); |
| |
| String getAccessor = JAXBUtils.nameToIdentifier(partName, JAXBUtils.IdentifierType.GETTER); |
| String setAccessor = JAXBUtils.nameToIdentifier(partName, JAXBUtils.IdentifierType.SETTER); |
| Method getMethod = null; |
| Method setMethod = null; |
| Class<?> valueClass = wrapperType; |
| |
| try { |
| getMethod = valueClass.getMethod(getAccessor, AbstractWrapperHelper.NO_CLASSES); |
| } catch (NoSuchMethodException ex) { |
| //ignore for now |
| } |
| |
| Field elField = getElField(partName, valueClass); |
| if (getMethod == null |
| && elementType != null |
| && "boolean".equalsIgnoreCase(elementType) |
| && (elField == null |
| || (!Collection.class.isAssignableFrom(elField.getType()) |
| && !elField.getType().isArray()))) { |
| |
| try { |
| String newAcc = getAccessor.replaceFirst("get", "is"); |
| getMethod = wrapperType.getMethod(newAcc, AbstractWrapperHelper.NO_CLASSES); |
| } catch (NoSuchMethodException ex) { |
| //ignore for now |
| } |
| } |
| if (getMethod == null |
| && "return".equals(partName)) { |
| //RI generated code uses this |
| try { |
| getMethod = valueClass.getMethod("get_return", AbstractWrapperHelper.NO_CLASSES); |
| } catch (NoSuchMethodException ex) { |
| try { |
| getMethod = valueClass.getMethod("is_return", |
| new Class[0]); |
| } catch (NoSuchMethodException ex2) { |
| //ignore for now |
| } |
| } |
| } |
| if (getMethod == null && elField != null) { |
| getAccessor = JAXBUtils.nameToIdentifier(elField.getName(), JAXBUtils.IdentifierType.GETTER); |
| setAccessor = JAXBUtils.nameToIdentifier(elField.getName(), JAXBUtils.IdentifierType.SETTER); |
| try { |
| getMethod = valueClass.getMethod(getAccessor, AbstractWrapperHelper.NO_CLASSES); |
| } catch (NoSuchMethodException ex) { |
| //ignore for now |
| } |
| } |
| String setAccessor2 = setAccessor; |
| if ("return".equals(partName)) { |
| //some versions of jaxb map "return" to "set_return" instead of "setReturn" |
| setAccessor2 = "set_return"; |
| } |
| |
| for (Method method : allMethods) { |
| if (method.getParameterTypes() != null && method.getParameterTypes().length == 1 |
| && (setAccessor.equals(method.getName()) |
| || setAccessor2.equals(method.getName()))) { |
| setMethod = method; |
| break; |
| } |
| } |
| |
| getMethods.add(getMethod); |
| setMethods.add(setMethod); |
| if (setMethod != null |
| && JAXBElement.class.isAssignableFrom(setMethod.getParameterTypes()[0])) { |
| |
| Type t = setMethod.getGenericParameterTypes()[0]; |
| Class<?> pcls = null; |
| if (t instanceof ParameterizedType) { |
| t = ((ParameterizedType)t).getActualTypeArguments()[0]; |
| } |
| if (t instanceof Class) { |
| pcls = (Class<?>)t; |
| } |
| |
| String methodName = "create" + wrapperType.getSimpleName() |
| + setMethod.getName().substring(3); |
| |
| for (Method m : allOFMethods) { |
| if (m.getName().equals(methodName) |
| && m.getParameterTypes().length == 1 |
| && (pcls == null |
| || pcls.equals(m.getParameterTypes()[0]))) { |
| jaxbMethods.add(m); |
| } |
| } |
| } else { |
| jaxbMethods.add(null); |
| } |
| |
| if (elField != null) { |
| // JAXB Type get XmlElement Annotation |
| XmlElement el = elField.getAnnotation(XmlElement.class); |
| if (el != null |
| && (partName.equals(el.name()) |
| || "##default".equals(el.name()))) { |
| ReflectionUtil.setAccessible(elField); |
| fields.add(elField); |
| } else { |
| if (getMethod == null && setMethod == null) { |
| if (el != null) { |
| LOG.warning("Could not create accessor for property " + partName |
| + " of type " + wrapperType.getName() + " as the @XmlElement " |
| + "defines the name as " + el.name()); |
| } else { |
| LOG.warning("Could not create accessor for property " + partName |
| + " of type " + wrapperType.getName()); |
| } |
| } |
| fields.add(null); |
| } |
| } else { |
| fields.add(null); |
| } |
| |
| } |
| |
| return createWrapperHelper(getBus(), wrapperType, |
| setMethods.toArray(new Method[0]), |
| getMethods.toArray(new Method[0]), |
| jaxbMethods.toArray(new Method[0]), |
| fields.toArray(new Field[0]), |
| objectFactory); |
| } |
| |
| public static boolean isInBuiltInSchemas(DOMResult schema) { |
| return BUILT_IN_SCHEMAS.containsValue(schema); |
| } |
| |
| private static Field getElField(String partName, final Class<?> wrapperType) { |
| String fieldName = JAXBUtils.nameToIdentifier(partName, JAXBUtils.IdentifierType.VARIABLE); |
| Field[] fields = ReflectionUtil.getDeclaredFields(wrapperType); |
| for (Field field : fields) { |
| XmlElement el = field.getAnnotation(XmlElement.class); |
| if (el != null |
| && partName.equals(el.name())) { |
| return field; |
| } |
| |
| XmlElementRef xmlElementRefAnnotation = field.getAnnotation(XmlElementRef.class); |
| if (xmlElementRefAnnotation != null && partName.equals(xmlElementRefAnnotation.name())) { |
| return field; |
| } |
| |
| if (field.getName().equals(fieldName)) { |
| return field; |
| } |
| } |
| return null; |
| } |
| |
| |
| private static WrapperHelper createWrapperHelper(Bus bus, Class<?> wrapperType, Method[] setMethods, |
| Method[] getMethods, Method[] jaxbMethods, |
| Field[] fields, Object objectFactory) { |
| |
| WrapperHelper wh = compileWrapperHelper(bus, wrapperType, setMethods, getMethods, jaxbMethods, fields, |
| objectFactory); |
| |
| if (wh == null) { |
| wh = new JAXBWrapperHelper(wrapperType, setMethods, getMethods, jaxbMethods, fields, |
| objectFactory); |
| } |
| return wh; |
| } |
| |
| private static WrapperHelper compileWrapperHelper(Bus bus, Class<?> wrapperType, Method[] setMethods, |
| Method[] getMethods, Method[] jaxbMethods, |
| Field[] fields, Object objectFactory) { |
| try { |
| WrapperHelperCreator creator = bus.getExtension(WrapperHelperCreator.class); |
| return creator.compile(wrapperType, setMethods, getMethods, |
| jaxbMethods, fields, objectFactory); |
| } catch (Throwable t) { |
| // Some error - probably a bad version of ASM or similar |
| return null; |
| } |
| } |
| |
| } |