You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2019/03/15 12:51:47 UTC

[camel] branch master updated: Fix for CAMEL-13282

This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new 7c1e0a2  Fix for CAMEL-13282
7c1e0a2 is described below

commit 7c1e0a2e561eaa1d6d865863ea92441fbcc2e4d0
Author: Michael Lück <mi...@lueckonline.net>
AuthorDate: Sun Mar 10 21:34:01 2019 +0100

    Fix for CAMEL-13282
    
    When a JAXB class that is created or generated has no @XmlRootElement
    annotation JaxbDataFormat would try to find a ObjectFactory method which
    can create a JAXBElement for the type of the object. It would then use
    the JAXBElement to marshal the object.
    
    While trying to find the objectFactory and to call the appropriate
    method it would catch ALL Exceptions that are thrown and just logs a
    message.
    
    While i don't see why an Exception should only be logged here this
    breaks when the exception is thrown due to a validation error. Because
    the calls to the marshal operation will throw an MarshalException in
    that case which would be caught by the catch block. In other cases where
    no object factory is used but JAXB classes with XmlRootElement
    Annotations or JAXBElements are used the MarshalExceptions will be
    thrown and the doMarshal method will be left.
    
    So i basically check the type of the exception that is thrown and
    rethrow it if it's a MarshalException. All other exceptions are handled
    in the same way as before. I kept the old exception handling in order to
    keep the code as much backwards compatible as possible. But it should be
    removed for 3.0.0 at the latest
---
 .../camel/converter/jaxb/JaxbDataFormat.java       |  13 ++-
 .../JaxbDataFormatSchemaValidationSpringTest.java  |  74 +++++++++++--
 ...ormatSchemaValidationTestWithObjectFactory.java | 116 +++++++++++++++++++++
 .../converter/jaxb/message/ObjectFactory.java      |   4 +-
 .../camel/converter/jaxb/message/package-info.java |   2 +-
 .../camel-jaxb/src/test/resources/message.xml      |  22 ++++
 .../camel-jaxb/src/test/resources/message.xsd      |  14 +++
 .../org/apache/camel/converter/jaxb/context.xml    |   4 +-
 8 files changed, 232 insertions(+), 17 deletions(-)

diff --git a/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java b/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
index 592c2a9..8577bf9 100644
--- a/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
+++ b/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
@@ -35,6 +35,7 @@ import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.JAXBIntrospector;
+import javax.xml.bind.MarshalException;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 import javax.xml.bind.ValidationEvent;
@@ -63,7 +64,8 @@ import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
-
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A <a href="http://camel.apache.org/data-format.html">data format</a> ({@link DataFormat})
@@ -237,6 +239,15 @@ public class JaxbDataFormat extends ServiceSupport implements DataFormat, DataFo
                         return;
                     }
                 } catch (Exception e) {
+                    // if a schema is set then an MarshallException is thrown when the XML is not valid
+                    // and the method must throw this exception as it would when the object in the body is a root element
+                    // or a partial class (the other alternatives above)
+                    // 
+                    // it would be best to completely remove the exception handler here but it's left for backwards compatibility reasons.
+                    if (MarshalException.class.isAssignableFrom(e.getClass()) && schema != null) {
+                        throw e;
+                    }
+                    
                     log.debug("Unable to create JAXBElement object for type " + element.getClass() + " due to " + e.getMessage(), e);
                 }
             }
diff --git a/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationSpringTest.java b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationSpringTest.java
index 8af1321..d33d5bd 100644
--- a/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationSpringTest.java
+++ b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationSpringTest.java
@@ -16,12 +16,21 @@
  */
 package org.apache.camel.converter.jaxb;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+
+import org.xml.sax.InputSource;
 
 import org.apache.camel.CamelExecutionException;
 import org.apache.camel.EndpointInject;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.converter.jaxb.address.Address;
+import org.apache.camel.converter.jaxb.message.Message;
+import org.apache.camel.converter.jaxb.message.ObjectFactory;
 import org.apache.camel.converter.jaxb.person.Person;
 import org.apache.camel.test.spring.CamelSpringTestSupport;
 import org.junit.Test;
@@ -36,10 +45,18 @@ public class JaxbDataFormatSchemaValidationSpringTest extends CamelSpringTestSup
     @EndpointInject(uri = "mock:unmarshall")
     private MockEndpoint mockUnmarshall;
 
+    private JAXBContext jbCtx;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        jbCtx = JAXBContext.newInstance(Person.class, Message.class);
+    }
+
     @Test
     public void testMarshallSuccess() throws Exception {
         mockMarshall.expectedMessageCount(1);
-
+        
         Address address = new Address();
         address.setAddressLine1("Hauptstr. 1; 01129 Entenhausen");
         Person person = new Person();
@@ -54,16 +71,15 @@ public class JaxbDataFormatSchemaValidationSpringTest extends CamelSpringTestSup
 
         String payload = mockMarshall.getExchanges().get(0).getIn().getBody(String.class);
         log.info(payload);
-
-        assertTrue(payload.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"));
-        assertTrue(payload.contains("<person xmlns=\"person.jaxb.converter.camel.apache.org\" xmlns:ns2=\"address.jaxb.converter.camel.apache.org\">"));
-        assertTrue(payload.contains("<firstName>Christian</firstName>"));
-        assertTrue(payload.contains("<lastName>Mueller</lastName>"));
-        assertTrue(payload.contains("<age>36</age>"));
-        assertTrue(payload.contains("<address>"));
-        assertTrue(payload.contains("<ns2:addressLine1>Hauptstr. 1; 01129 Entenhausen</ns2:addressLine1>"));
-        assertTrue(payload.contains("</address>"));
-        assertTrue(payload.contains("</person>"));
+        
+        Person unmarshalledPerson = (Person) jbCtx.createUnmarshaller().unmarshal(new InputSource(new StringReader(payload)));
+
+        assertNotNull(unmarshalledPerson);
+        assertEquals(person.getFirstName(), unmarshalledPerson.getFirstName());
+        assertEquals(person.getLastName(), unmarshalledPerson.getLastName());
+        assertEquals(person.getAge(), unmarshalledPerson.getAge());
+        assertNotNull(unmarshalledPerson.getAddress());
+        assertEquals(person.getAddress().getAddressLine1(), unmarshalledPerson.getAddress().getAddressLine1());
     }
 
     @Test
@@ -122,6 +138,42 @@ public class JaxbDataFormatSchemaValidationSpringTest extends CamelSpringTestSup
             assertTrue(cause.getMessage().contains("cvc-complex-type.2.4.b"));
         }
     }
+    
+    @Test
+    public void testMarshallOfNonRootElementWithValidationException() throws Exception {
+        try {
+            template.sendBody("direct:marshall", new Message());
+            fail("CamelExecutionException expected");
+        } catch (CamelExecutionException e) {
+            Throwable cause = e.getCause();
+            assertIsInstanceOf(IOException.class, cause);
+            assertTrue(cause.getMessage().contains("javax.xml.bind.MarshalException"));
+            assertTrue(cause.getMessage().contains("org.xml.sax.SAXParseException"));
+            assertTrue(cause.getMessage().contains("cvc-complex-type.2.4.b"));
+        }
+    }
+    
+    @Test
+    public void testUnmarshallOfNonRootWithValidationException() throws Exception {
+        JAXBElement<Message> message = new ObjectFactory().createMessage(new Message());
+        
+        String xml;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            jbCtx.createMarshaller().marshal(message, baos);
+            xml = new String(baos.toByteArray(), "UTF-8");
+        }
+        
+        try {
+            template.sendBody("direct:unmarshall", xml);
+            fail("CamelExecutionException expected");
+        } catch (CamelExecutionException e) {
+            Throwable cause = e.getCause();
+            assertIsInstanceOf(IOException.class, cause);
+            assertTrue(cause.getMessage().contains("javax.xml.bind.UnmarshalException"));
+            assertTrue(cause.getMessage().contains("org.xml.sax.SAXParseException"));
+            assertTrue(cause.getMessage().contains("cvc-complex-type.2.4.b"));
+        }
+    }
 
     @Override
     protected AbstractApplicationContext createApplicationContext() {
diff --git a/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationTestWithObjectFactory.java b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationTestWithObjectFactory.java
new file mode 100644
index 0000000..2df30a4
--- /dev/null
+++ b/components/camel-jaxb/src/test/java/org/apache/camel/converter/jaxb/JaxbDataFormatSchemaValidationTestWithObjectFactory.java
@@ -0,0 +1,116 @@
+/**
+ * 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;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.converter.jaxb.message.Message;
+import org.apache.camel.converter.jaxb.message.ObjectFactory;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JaxbDataFormatSchemaValidationTestWithObjectFactory extends CamelTestSupport {
+
+    @EndpointInject(uri = "mock:marshall")
+    private MockEndpoint mockMarshall;
+
+    @EndpointInject(uri = "mock:unmarshall")
+    private MockEndpoint mockUnmarshall;
+
+    private JAXBContext jbCtx;
+    
+    @Override
+    public void setUp() throws Exception {
+        
+        super.setUp();
+        
+        XmlRootElement xmlRootElementAnnotation = Message.class.getAnnotation(XmlRootElement.class);
+        assertNull(xmlRootElementAnnotation);
+      
+        jbCtx = JAXBContext.newInstance(Message.class);
+    }
+
+    @Test
+    public void testMarshallOfNonRootElementWithValidationException() throws Exception {
+        try {
+            template.sendBody("direct:marshall", new Message());
+            fail("CamelExecutionException expected");
+        } catch (CamelExecutionException e) {
+            Throwable cause = e.getCause();
+            assertIsInstanceOf(IOException.class, cause);
+            assertTrue(cause.getMessage().contains("javax.xml.bind.MarshalException"));
+            assertTrue(cause.getMessage().contains("org.xml.sax.SAXParseException"));
+            assertTrue(cause.getMessage().contains("cvc-complex-type.2.4.b"));
+        }
+    } 
+    
+    @Test
+    public void testUnmarshallOfNonRootWithValidationException() throws Exception {
+        
+        JAXBElement<Message> message = new ObjectFactory().createMessage(new Message());
+        
+        String xml;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            jbCtx.createMarshaller().marshal(message, baos);
+            xml = new String(baos.toByteArray(), "UTF-8");
+        }
+        
+        try {
+            template.sendBody("direct:unmarshall", xml);
+            fail("CamelExecutionException expected");
+        } catch (CamelExecutionException e) {
+            Throwable cause = e.getCause();
+            assertIsInstanceOf(IOException.class, cause);
+            assertTrue(cause.getMessage().contains("javax.xml.bind.UnmarshalException"));
+            assertTrue(cause.getMessage().contains("org.xml.sax.SAXParseException"));
+            assertTrue(cause.getMessage().contains("cvc-complex-type.2.4.b"));
+        }
+    }
+    
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                JaxbDataFormat jaxbDataFormat = new JaxbDataFormat();
+                jaxbDataFormat.setContextPath(Message.class.getPackage().getName());
+                jaxbDataFormat.setSchema("classpath:message.xsd");
+                // if the following is removed the lookup of an object factory method which can create the element
+                // won't be done and the object won'T get marshalled
+                jaxbDataFormat.setObjectFactory(true);
+
+                from("direct:marshall")
+                    .marshal(jaxbDataFormat)
+                    .to("mock:marshall");
+
+                from("direct:unmarshall")
+                    .unmarshal(jaxbDataFormat)
+                    .to("mock:unmarshall");
+            }
+        };
+    }
+
+}
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
index bc53862..1f17b1e 100644
--- 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
@@ -38,7 +38,7 @@ import javax.xml.namespace.QName;
 @XmlRegistry
 public class ObjectFactory {
 
-    private static final QName MESSAGE_QNAME = new QName("", "message");
+    private static final QName MESSAGE_QNAME = new QName("message.jaxb.converter.camel.apache.org", "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
@@ -53,7 +53,7 @@ public class ObjectFactory {
         return new Message();
     }
 
-    @XmlElementDecl(namespace = "", name = "message")
+    @XmlElementDecl(namespace = "message.jaxb.converter.camel.apache.org", name = "message")
     public JAXBElement<Message> createMessage(Message value) {
         return new JAXBElement<>(MESSAGE_QNAME, Message.class, null, value);
     }
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
index 3be7aa0..4d716cd 100644
--- 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
@@ -14,5 +14,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@javax.xml.bind.annotation.XmlSchema(namespace = "")
+@javax.xml.bind.annotation.XmlSchema(namespace = "message.jaxb.converter.camel.apache.org")
 package org.apache.camel.converter.jaxb.message;
diff --git a/components/camel-jaxb/src/test/resources/message.xml b/components/camel-jaxb/src/test/resources/message.xml
new file mode 100644
index 0000000..6afe321
--- /dev/null
+++ b/components/camel-jaxb/src/test/resources/message.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<tns:message xmlns:tns="address.jaxb.converter.camel.apache.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="address.jaxb.converter.camel.apache.org message.xsd ">
+  <tns:text>tns:text</tns:text>
+</tns:message>
diff --git a/components/camel-jaxb/src/test/resources/message.xsd b/components/camel-jaxb/src/test/resources/message.xsd
new file mode 100644
index 0000000..f2b2105
--- /dev/null
+++ b/components/camel-jaxb/src/test/resources/message.xsd
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+	targetNamespace="message.jaxb.converter.camel.apache.org"
+	xmlns:tns="message.jaxb.converter.camel.apache.org"
+	elementFormDefault="qualified">
+
+	<element name="message" type="tns:Message"></element>
+
+	<complexType name="Message">
+		<sequence>
+			<element name="text" minOccurs="1" type="string" />
+		</sequence>
+	</complexType>
+</schema>
\ No newline at end of file
diff --git a/components/camel-jaxb/src/test/resources/org/apache/camel/converter/jaxb/context.xml b/components/camel-jaxb/src/test/resources/org/apache/camel/converter/jaxb/context.xml
index 62a56f6..10f46e7 100644
--- a/components/camel-jaxb/src/test/resources/org/apache/camel/converter/jaxb/context.xml
+++ b/components/camel-jaxb/src/test/resources/org/apache/camel/converter/jaxb/context.xml
@@ -27,7 +27,7 @@
         <route>
             <from uri="direct:marshall" />
             <marshal>
-                <jaxb contextPath="org.apache.camel.converter.jaxb.person" schema="classpath:person.xsd,classpath:address.xsd" />
+                <jaxb contextPath="org.apache.camel.converter.jaxb.person:org.apache.camel.converter.jaxb.message" schema="classpath:person.xsd,classpath:address.xsd,classpath:message.xsd" />
             </marshal>
             <to uri="mock:marshall" />
         </route>
@@ -35,7 +35,7 @@
         <route>
             <from uri="direct:unmarshall" />
             <unmarshal>
-                <jaxb contextPath="org.apache.camel.converter.jaxb.person" schema="classpath:person.xsd,classpath:address.xsd" />
+                <jaxb contextPath="org.apache.camel.converter.jaxb.person:org.apache.camel.converter.jaxb.message" schema="classpath:person.xsd,classpath:address.xsd,classpath:message.xsd" />
             </unmarshal>
             <to uri="mock:unmarshall" />
         </route>