Return-Path: X-Original-To: apmail-camel-commits-archive@www.apache.org Delivered-To: apmail-camel-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 57ABE18919 for ; Mon, 7 Mar 2016 07:34:58 +0000 (UTC) Received: (qmail 83500 invoked by uid 500); 7 Mar 2016 07:34:58 -0000 Delivered-To: apmail-camel-commits-archive@camel.apache.org Received: (qmail 83451 invoked by uid 500); 7 Mar 2016 07:34:58 -0000 Mailing-List: contact commits-help@camel.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@camel.apache.org Delivered-To: mailing list commits@camel.apache.org Received: (qmail 83439 invoked by uid 99); 7 Mar 2016 07:34:58 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Mar 2016 07:34:58 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 20C35DFC55; Mon, 7 Mar 2016 07:34:58 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: davsclaus@apache.org To: commits@camel.apache.org Message-Id: <53d28f7935b646e58988579dce17a1a3@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: camel git commit: CAMEL-9649: Do not require @XmlRootElement annotation in JAXB FallbackTypeConverter Date: Mon, 7 Mar 2016 07:34:58 +0000 (UTC) Repository: camel Updated Branches: refs/heads/master e6cad7c0b -> e413b9103 CAMEL-9649: Do not require @XmlRootElement annotation in JAXB FallbackTypeConverter Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/e413b910 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/e413b910 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/e413b910 Branch: refs/heads/master Commit: e413b9103973fbab68e25376e3f5107dca9097d2 Parents: e6cad7c Author: Martin Basovnik Authored: Wed Mar 2 09:31:26 2016 +0100 Committer: Claus Ibsen Committed: Mon Mar 7 08:30:22 2016 +0100 ---------------------------------------------------------------------- .../converter/jaxb/FallbackTypeConverter.java | 106 ++++++++++++++++--- .../camel/converter/jaxb/message/Message.java | 80 ++++++++++++++ .../converter/jaxb/message/ObjectFactory.java | 61 +++++++++++ .../converter/jaxb/message/package-info.java | 18 ++++ .../jaxb/CamelJaxbFallbackConverterTest.java | 9 ++ 5 files changed, 259 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/e413b910/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java ---------------------------------------------------------------------- diff --git a/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java b/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java index 9b3baac..960bfb6 100644 --- a/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java +++ b/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java @@ -24,13 +24,17 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; 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.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLStreamException; @@ -38,6 +42,8 @@ import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Source; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; import org.apache.camel.Exchange; import org.apache.camel.NoTypeConversionAvailableException; import org.apache.camel.Processor; @@ -55,13 +61,14 @@ import org.slf4j.LoggerFactory; /** * @version */ -public class FallbackTypeConverter extends ServiceSupport implements TypeConverter, TypeConverterAware { +public class FallbackTypeConverter extends ServiceSupport implements TypeConverter, TypeConverterAware, CamelContextAware { public static final String PRETTY_PRINT = "CamelJaxbPrettyPrint"; private static final Logger LOG = LoggerFactory.getLogger(FallbackTypeConverter.class); - private final Map, JAXBContext> contexts = new HashMap, JAXBContext>(); + private final Map contexts = new HashMap<>(); private final StaxConverter staxConverter = new StaxConverter(); private TypeConverter parentTypeConverter; private boolean prettyPrint = true; + private CamelContext camelContext; public boolean isPrettyPrint() { return prettyPrint; @@ -79,6 +86,14 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert this.parentTypeConverter = parentTypeConverter; } + public CamelContext getCamelContext() { + return camelContext; + } + + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + public T convertTo(Class type, Object value) { return convertTo(type, null, value); } @@ -145,9 +160,20 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert contexts.clear(); } + private boolean hasXmlRootElement(Class type) { + return type.getAnnotation(XmlRootElement.class) != null; + } + protected boolean isJaxbType(Class type) { - XmlRootElement element = type.getAnnotation(XmlRootElement.class); - return element != null; + return hasXmlRootElement(type) || (getJaxbElementFactoryMethod(type) != null); + } + + private T castJaxbType(Object o, Class type) { + if (type.isAssignableFrom(o.getClass())) { + return type.cast(o); + } else { + return type.cast(((JAXBElement) o).getValue()); + } } /** @@ -169,7 +195,7 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert if (xmlReader != null) { try { Object unmarshalled = unmarshal(unmarshaller, exchange, xmlReader); - return type.cast(unmarshalled); + return castJaxbType(unmarshalled, type); } catch (Exception ex) { // There is some issue on the StaxStreamReader to CXFPayload message body with different namespaces LOG.debug("Cannot use StaxStreamReader to unmarshal the message, due to {}", ex); @@ -179,17 +205,17 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert InputStream inputStream = parentTypeConverter.convertTo(InputStream.class, exchange, value); if (inputStream != null) { Object unmarshalled = unmarshal(unmarshaller, exchange, inputStream); - return type.cast(unmarshalled); + return castJaxbType(unmarshalled, type); } Reader reader = parentTypeConverter.convertTo(Reader.class, exchange, value); if (reader != null) { Object unmarshalled = unmarshal(unmarshaller, exchange, reader); - return type.cast(unmarshalled); + return castJaxbType(unmarshalled, type); } Source source = parentTypeConverter.convertTo(Source.class, exchange, value); if (source != null) { Object unmarshalled = unmarshal(unmarshaller, exchange, source); - return type.cast(unmarshalled); + return castJaxbType(unmarshalled, type); } } @@ -198,7 +224,7 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert } if (value instanceof InputStream || value instanceof Reader) { Object unmarshalled = unmarshal(unmarshaller, exchange, value); - return type.cast(unmarshalled); + return castJaxbType(unmarshalled, type); } return null; @@ -234,17 +260,25 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert if (exchange != null && exchange.getProperty(Exchange.CHARSET_NAME, String.class) != null) { marshaller.setProperty(Marshaller.JAXB_ENCODING, exchange.getProperty(Exchange.CHARSET_NAME, String.class)); } + Object toMarshall = value; + if (!hasXmlRootElement(value.getClass())) { + Method m = getJaxbElementFactoryMethod(value.getClass()); + try { + toMarshall = m.invoke(getObjectFactory(value.getClass()).newInstance(), value); + } catch (Exception e) { + LOG.error("Unable to create JAXBElement object for type {} due to {}", value.getClass().getName(), e); + } + } if (needFiltering(exchange)) { XMLStreamWriter writer = parentTypeConverter.convertTo(XMLStreamWriter.class, buffer); FilteringXmlStreamWriter filteringWriter = new FilteringXmlStreamWriter(writer); - marshaller.marshal(value, filteringWriter); + marshaller.marshal(toMarshall, filteringWriter); } else { - marshaller.marshal(value, buffer); + marshaller.marshal(toMarshall, buffer); } // we need to pass the exchange answer = parentTypeConverter.convertTo(type, exchange, buffer.toString()); } - return answer; } @@ -287,10 +321,16 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert } protected synchronized JAXBContext createContext(Class type) throws JAXBException { - JAXBContext context = contexts.get(type); + AnnotatedElement ae = hasXmlRootElement(type) ? type : type.getPackage(); + JAXBContext context = contexts.get(ae); if (context == null) { - context = JAXBContext.newInstance(type); - contexts.put(type, context); + if (hasXmlRootElement(type)) { + context = JAXBContext.newInstance(type); + contexts.put(type, context); + } else { + context = JAXBContext.newInstance(type.getPackage().getName()); + contexts.put(type.getPackage(), context); + } } return context; } @@ -304,4 +344,40 @@ public class FallbackTypeConverter extends ServiceSupport implements TypeConvert return !StreamCache.class.isAssignableFrom(type); } + private Class getObjectFactory(Class type) throws ClassNotFoundException { + Class c = null; + if (type.getPackage() != null) { + String objectFactoryClassName = type.getPackage().getName() + ".ObjectFactory"; + c = camelContext.getClassResolver().resolveClass(objectFactoryClassName); + } + if (c == null) { + throw new ClassNotFoundException(String.format("ObjectFactory for type %s was not found", type.getName())); + } else { + return c; + } + } + + private Method getJaxbElementFactoryMethod(Class type) { + Method factoryMethod = null; + try { + for (Method m : getObjectFactory(type).getMethods()) { + final XmlElementDecl a = m.getAnnotation(XmlElementDecl.class); + if (a == null) { + continue; + } + final Class[] parameters = m.getParameterTypes(); + if (parameters.length == 1 && parameters[0].isAssignableFrom(type)) { + if (factoryMethod != null) { + throw new IllegalStateException("There are several possible XML schema mappings for class " + type.getName()); + } else { + factoryMethod = m; + } + } + } + } catch (ClassNotFoundException e) { + LOG.debug(e.getMessage(), e); + } + return factoryMethod; + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/e413b910/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/Message.java ---------------------------------------------------------------------- diff --git a/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/Message.java b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/Message.java new file mode 100644 index 0000000..51be022 --- /dev/null +++ b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/Message.java @@ -0,0 +1,80 @@ +/** + * 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.camel.converter.jaxb.message; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = {"text"}) +public class Message { + + @XmlElement(required = true) + private String text; + + public Message() { + } + + public Message(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Message)) { + return false; + } + Message message = (Message) o; + return getText() != null ? getText().equals(message.getText()) : message.getText() == null; + } + + @Override + public int hashCode() { + return getText() != null ? getText().hashCode() : 0; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e413b910/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/ObjectFactory.java ---------------------------------------------------------------------- diff --git a/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/ObjectFactory.java b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/ObjectFactory.java new file mode 100644 index 0000000..bc53862 --- /dev/null +++ b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/ObjectFactory.java @@ -0,0 +1,61 @@ +/** + * 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.camel.converter.jaxb.message; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.apache.camel.converter.jaxb.message package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + */ +@XmlRegistry +public class ObjectFactory { + + private static final QName MESSAGE_QNAME = new QName("", "message"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.apache.camel.converter.jaxb.message + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Message } + */ + public Message createMessage() { + return new Message(); + } + + @XmlElementDecl(namespace = "", name = "message") + public JAXBElement createMessage(Message value) { + return new JAXBElement<>(MESSAGE_QNAME, Message.class, null, value); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/e413b910/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/package-info.java ---------------------------------------------------------------------- diff --git a/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/package-info.java b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/package-info.java new file mode 100644 index 0000000..3be7aa0 --- /dev/null +++ b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/message/package-info.java @@ -0,0 +1,18 @@ +/** + * 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. + */ +@javax.xml.bind.annotation.XmlSchema(namespace = "") +package org.apache.camel.converter.jaxb.message; http://git-wip-us.apache.org/repos/asf/camel/blob/e413b910/components/camel-jaxb/src/test/java/org/apache/camel/jaxb/CamelJaxbFallbackConverterTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jaxb/src/test/java/org/apache/camel/jaxb/CamelJaxbFallbackConverterTest.java b/components/camel-jaxb/src/test/java/org/apache/camel/jaxb/CamelJaxbFallbackConverterTest.java index f956bad..c4559b3 100644 --- a/components/camel-jaxb/src/test/java/org/apache/camel/jaxb/CamelJaxbFallbackConverterTest.java +++ b/components/camel-jaxb/src/test/java/org/apache/camel/jaxb/CamelJaxbFallbackConverterTest.java @@ -22,6 +22,7 @@ import java.io.InputStream; import org.apache.camel.Exchange; import org.apache.camel.TypeConversionException; import org.apache.camel.TypeConverter; +import org.apache.camel.converter.jaxb.message.Message; import org.apache.camel.example.Bar; import org.apache.camel.example.Foo; import org.apache.camel.foo.bar.PersonType; @@ -115,7 +116,15 @@ public class CamelJaxbFallbackConverterTest extends CamelTestSupport { value = converter.convertTo(String.class, exchange, person); assertTrue("Should not filter the non-xml chars", value.indexOf("BAR\uD8FF") > 0); + } + @Test + public void testNoXmlRootElementAnnotation() throws Exception { + Message in = new Message("Hello World"); + TypeConverter converter = context.getTypeConverter(); + String marshalled = converter.convertTo(String.class, in); + Message out = converter.convertTo(Message.class, marshalled); + assertEquals(in, out); } }