This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit 420f0fdfbe68d5f6877867e52bd09c82a0b1ec3e Author: James Netherton <[email protected]> AuthorDate: Wed Jul 6 12:51:26 2022 +0100 Increase JAXB extension test coverage Fixes #3896 --- .../ROOT/pages/reference/extensions/jaxb.adoc | 11 + extensions/jaxb/runtime/src/main/doc/usage.adoc | 7 + integration-tests/jaxb/pom.xml | 4 + .../{model/ObjectFactory.java => JaxbHelper.java} | 28 +- .../quarkus/component/jaxb/it/JaxbProducers.java | 130 +++++++ .../quarkus/component/jaxb/it/JaxbResource.java | 338 +++++++++++++++--- .../camel/quarkus/component/jaxb/it/JaxbRoute.java | 121 ++++++- .../jaxb/it/converter/PojoPersonConverter.java | 44 +++ .../quarkus/component/jaxb/it/model/Person.java | 99 +---- .../model/factory/FactoryInstantiatedPerson.java | 68 ++++ .../jaxb/it/model/{ => factory}/ObjectFactory.java | 23 +- .../NamespacedPerson.java} | 84 ++--- .../it/model/{ => namespaced}/ObjectFactory.java | 13 +- .../jaxb/it/model/{ => partial}/ObjectFactory.java | 13 +- .../jaxb/it/model/partial/PartClassPerson.java | 77 ++++ .../package-info.java} | 26 +- .../component/jaxb/it/model/pojo/PojoPerson.java | 65 ++++ .../jaxb/it/model/{ => simple}/ObjectFactory.java | 22 +- .../{Person.java => simple/SimplePerson.java} | 84 ++--- .../jaxb/it/writer/CustomXmlStreamWriter.java | 199 +++++++++++ .../jaxb/src/main/resources/application.properties | 11 +- .../component/jaxb/it/model/pojo/jaxb.index} | 10 +- .../jaxb/src/main/resources/person.xsd | 7 +- .../camel/quarkus/component/jaxb/it/JaxbTest.java | 397 +++++++++++++++++++-- pom.xml | 1 + 25 files changed, 1466 insertions(+), 416 deletions(-) diff --git a/docs/modules/ROOT/pages/reference/extensions/jaxb.adoc b/docs/modules/ROOT/pages/reference/extensions/jaxb.adoc index 890d7abe76..75eca7ed99 100644 --- a/docs/modules/ROOT/pages/reference/extensions/jaxb.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/jaxb.adoc @@ -38,3 +38,14 @@ Or add the coordinates to your existing project: ---- Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications. + +== Usage + +=== Native mode `ObjectFactory` instantiation of non-JAXB annotated classes + +When performing JAXB marshal operations with a custom `ObjectFactory` to instantiate POJO classes that do not have JAXB annotations, +you must register those POJO classes for reflection in order for them to be instantiated in native mode. E.g via the `@RegisterForReflection` +annotation or configuration property `quarkus.camel.native.reflection.include-patterns`. + +Refer to the xref:user-guide/native-mode.adoc#reflection[Native mode] user guide for more information. + diff --git a/extensions/jaxb/runtime/src/main/doc/usage.adoc b/extensions/jaxb/runtime/src/main/doc/usage.adoc new file mode 100644 index 0000000000..6e56d0fdd4 --- /dev/null +++ b/extensions/jaxb/runtime/src/main/doc/usage.adoc @@ -0,0 +1,7 @@ +=== Native mode `ObjectFactory` instantiation of non-JAXB annotated classes + +When performing JAXB marshal operations with a custom `ObjectFactory` to instantiate POJO classes that do not have JAXB annotations, +you must register those POJO classes for reflection in order for them to be instantiated in native mode. E.g via the `@RegisterForReflection` +annotation or configuration property `quarkus.camel.native.reflection.include-patterns`. + +Refer to the xref:user-guide/native-mode.adoc#reflection[Native mode] user guide for more information. diff --git a/integration-tests/jaxb/pom.xml b/integration-tests/jaxb/pom.xml index 6d81694e56..2be764b776 100644 --- a/integration-tests/jaxb/pom.xml +++ b/integration-tests/jaxb/pom.xml @@ -43,6 +43,10 @@ <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-rest-client-jsonb</artifactId> + </dependency> <!-- test dependencies --> <dependency> diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbHelper.java similarity index 60% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbHelper.java index 46f83efa48..9d13549aef 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbHelper.java @@ -14,24 +14,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it; -import javax.xml.bind.annotation.XmlRegistry; +import javax.json.Json; +import javax.json.JsonObject; -@XmlRegistry -public class ObjectFactory { +import org.apache.camel.quarkus.component.jaxb.it.model.Person; - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.apache.camel.converter.jaxb.person - */ - public ObjectFactory() { +public final class JaxbHelper { + + private JaxbHelper() { + // Utility class } - /** - * Create an instance of {@link Person } - */ - public Person createPerson() { - return new Person(); + public static JsonObject personToJson(Person person) { + return Json.createObjectBuilder() + .add("firstName", person.getFirstName()) + .add("lastName", person.getLastName()) + .add("age", person.getAge()) + .build(); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbProducers.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbProducers.java new file mode 100644 index 0000000000..956d76c255 --- /dev/null +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbProducers.java @@ -0,0 +1,130 @@ +/* + * 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.quarkus.component.jaxb.it; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.xml.bind.Marshaller; +import javax.xml.namespace.QName; + +import org.apache.camel.converter.jaxb.JaxbDataFormat; +import org.apache.camel.quarkus.component.jaxb.it.model.namespaced.NamespacedPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.partial.PartClassPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.pojo.PojoPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.simple.SimplePerson; +import org.apache.camel.quarkus.component.jaxb.it.writer.CustomXmlStreamWriter; + +public class JaxbProducers { + + @Singleton + @Named("jaxbDefault") + public JaxbDataFormat defaultJaxbDataFormat() { + JaxbDataFormat dataFormat = new JaxbDataFormat(); + dataFormat.setContextPath(SimplePerson.class.getPackageName()); + dataFormat.setFragment(true); + dataFormat.setIgnoreJAXBElement(false); + dataFormat.setPrettyPrint(false); + dataFormat.setSchema("classpath:person.xsd"); + dataFormat.setSchemaSeverityLevel(2); + return dataFormat; + } + + @Singleton + @Named("jaxbWithNamespacePrefix") + public JaxbDataFormat jaxbDataFormatWithNamespacePrefix() { + JaxbDataFormat dataFormat = new JaxbDataFormat(); + dataFormat.setContextPath(NamespacedPerson.class.getPackageName()); + dataFormat.setNamespacePrefix(Map.of("https://example.com/a", "test")); + return dataFormat; + } + + @Singleton + @Named("jaxbWithEncoding") + public JaxbDataFormat jaxbDataFormatWithCustomCharset() { + JaxbDataFormat dataFormat = new JaxbDataFormat(); + dataFormat.setContextPath(SimplePerson.class.getPackageName()); + dataFormat.setEncoding("ISO-8859-1"); + dataFormat.setFilterNonXmlChars(true); + return dataFormat; + } + + @Singleton + @Named("jaxbWithMustBeJAXBElementFalse") + public JaxbDataFormat jaxbDataFormatWithMustBeJAXBElementFalse() { + JaxbDataFormat dataFormat = new JaxbDataFormat(); + dataFormat.setMustBeJAXBElement(false); + return dataFormat; + } + + @Singleton + @Named("jaxbWithPartClass") + public JaxbDataFormat jaxbWithPartClass() { + JaxbDataFormat dataFormat = new JaxbDataFormat(); + dataFormat.setContextPath(PartClassPerson.class.getPackageName()); + dataFormat.setPartClass(PartClassPerson.class); + dataFormat.setPartNamespace(new QName(PartClassPerson.NAMESPACE, "person")); + return dataFormat; + } + + @Singleton + @Named("jaxbWithIgnoreElement") + public JaxbDataFormat jaxbWithIgnoreElement() { + JaxbDataFormat dataFormat = new JaxbDataFormat(); + dataFormat.setContextPath(PartClassPerson.class.getPackageName()); + dataFormat.setIgnoreJAXBElement(false); + dataFormat.setPartClass(PartClassPerson.class); + dataFormat.setPartNamespace(new QName(PartClassPerson.NAMESPACE, "person")); + return dataFormat; + } + + @Singleton + @Named("jaxbWithCustomProperties") + public JaxbDataFormat jaxbWithCustomProperties() { + String packages = String.format("%s:%s", + SimplePerson.class.getPackageName(), + NamespacedPerson.class.getPackageName()); + JaxbDataFormat dataFormat = new JaxbDataFormat(packages); + dataFormat.setJaxbProviderProperties(Map.of(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE)); + return dataFormat; + } + + @Singleton + @Named("jaxbWithCustomStreamWriter") + public JaxbDataFormat jaxbWithCustomStreamWriter() { + JaxbDataFormat dataFormat = new JaxbDataFormat(SimplePerson.class.getPackageName()); + dataFormat.setXmlStreamWriterWrapper(new CustomXmlStreamWriter()); + return dataFormat; + } + + @Singleton + @Named("jaxbWithoutObjectFactory") + public JaxbDataFormat jaxbWithoutObjectFactory() { + JaxbDataFormat dataFormat = new JaxbDataFormat(PojoPerson.class.getPackageName()); + dataFormat.setObjectFactory(false); + return dataFormat; + } + + @Singleton + @Named("jaxbWithNoNamespaceSchemaLocation") + public JaxbDataFormat jaxbWithNoNamespaceSchemaLocation() { + JaxbDataFormat dataFormat = new JaxbDataFormat(SimplePerson.class.getPackageName()); + dataFormat.setNoNamespaceSchemaLocation("person-no-namespace.xsd"); + return dataFormat; + } +} diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbResource.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbResource.java index 6c37679dcf..b876e4d8f1 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbResource.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbResource.java @@ -16,87 +16,321 @@ */ package org.apache.camel.quarkus.component.jaxb.it; -import java.net.URI; - import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.xml.bind.JAXBElement; +import org.apache.camel.Exchange; +import org.apache.camel.Message; +import org.apache.camel.Processor; import org.apache.camel.ProducerTemplate; import org.apache.camel.quarkus.component.jaxb.it.model.Person; -import org.jboss.logging.Logger; +import org.apache.camel.quarkus.component.jaxb.it.model.factory.FactoryInstantiatedPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.namespaced.NamespacedPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.partial.PartClassPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.pojo.PojoPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.simple.SimplePerson; +import org.apache.camel.util.ObjectHelper; + +import static org.apache.camel.quarkus.component.jaxb.it.JaxbHelper.personToJson; @Path("/jaxb") @ApplicationScoped public class JaxbResource { - private static final Logger LOG = Logger.getLogger(JaxbResource.class); - @Inject ProducerTemplate producerTemplate; - @Path("/unmarshal-lastname") + @Path("/marshal") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshal( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + Exchange result = producerTemplate.request("direct:marshal", new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.getMessage().setBody(person); + } + }); + + Exception exception = result.getException(); + if (exception != null) { + String xml = String.format("<error>%s</error>", exception.getMessage()); + return Response.serverError().entity(xml).build(); + } + + Message message = result.getMessage(); + String contentType = message.getHeader(Exchange.CONTENT_TYPE, String.class); + String xml = message.getBody(String.class); + + if (ObjectHelper.isEmpty(contentType) || !contentType.equals("application/xml")) { + throw new IllegalStateException("Expected content type application/xml but got " + contentType); + } + + if (xml.startsWith("<?xml")) { + throw new IllegalStateException("XML prolog was not expected as JaxbDataFormat.fragment = true"); + } + + return Response.ok(xml).build(); + } + + @Path("/unmarshal") @POST @Consumes(MediaType.APPLICATION_XML) - @Produces(MediaType.TEXT_PLAIN) - public Response unmarshalLastNameFromXml(String message) throws Exception { - LOG.infof("Sending to jaxb: %s", message); - final Person response = producerTemplate.requestBody("direct:unmarshal", message, Person.class); - LOG.infof("Got response from jaxb: %s", response); - return Response - .created(new URI("https://camel.apache.org/")) - .entity(response.getLastName()) - .build(); - } - - @Path("/unmarshal-firstname") + @Produces(MediaType.APPLICATION_JSON) + public Response unmarshal(String xml) { + Exchange result = producerTemplate.request("direct:unmarshal", new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.getMessage().setBody(xml); + } + }); + + Exception exception = result.getException(); + if (exception != null) { + String errorXml = String.format("{\"error\": \"%s\"}", exception.getMessage()); + return Response.serverError().entity(errorXml).build(); + } + + Person person = result.getMessage().getBody(SimplePerson.class); + return Response.ok(personToJson(person)).build(); + } + + @Path("/marshal/dsl") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalWithJaxbDsl( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalJaxbDsl", person, String.class); + + return Response.ok(xml).build(); + } + + @Path("/unmarshal/dsl") @POST @Consumes(MediaType.APPLICATION_XML) - @Produces(MediaType.TEXT_PLAIN) - public Response unmarshalFirstNameFromXml(String message) throws Exception { - LOG.infof("Sending to jaxb: %s", message); - final Person response = producerTemplate.requestBody("direct:unmarshal-2", message, Person.class); - LOG.infof("Got response from jaxb: %s", response); - return Response - .created(new URI("https://camel.apache.org/")) - .entity(response.getFirstName()) - .build(); - } - - @Path("/marshal-firstname") + @Produces(MediaType.APPLICATION_JSON) + public Response unmarshalWithJaxbDsl(String xml) { + Person person = producerTemplate.requestBody("direct:unmarshalJaxbDsl", xml, Person.class); + return Response.ok(personToJson(person)).build(); + } + + @Path("/marshal/namespace/prefix") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalWithNamespacePrefix( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new NamespacedPerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalNamespacePrefix", person, String.class); + + return Response.ok(xml).build(); + } + + @Path("/unmarshal/namespace/prefix") @POST - @Consumes(MediaType.TEXT_PLAIN) + @Consumes(MediaType.APPLICATION_XML) + @Produces(MediaType.APPLICATION_JSON) + public Response unmarshalWithNamespacePrefix(String xml) { + Person person = producerTemplate.requestBody("direct:unmarshalNamespacePrefix", xml, NamespacedPerson.class); + return Response.ok(personToJson(person)).build(); + } + + @Path("/marshal/encoding") + @GET @Produces(MediaType.APPLICATION_XML) - public Response marshallFirstName(String name) throws Exception { - Person p = new Person(); - p.setFirstName(name); + public Response marshalWithEncoding( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalEncoding", person, String.class); + + return Response.ok(xml).build(); + } - String response = producerTemplate.requestBody("direct:marshal", p, String.class); - LOG.infof("Got response from jaxb=>: %s", response); - return Response - .created(new URI("https://camel.apache.org/")) - .entity(response) - .build(); + @Path("/unmarshal/encoding") + @POST + @Consumes(MediaType.APPLICATION_XML) + @Produces(MediaType.APPLICATION_JSON) + public Response unmarshalWithEncoding(String xml) { + Person person = producerTemplate.requestBody("direct:unmarshalEncoding", xml, SimplePerson.class); + return Response.ok(personToJson(person)).build(); } - @Path("/marshal-lastname") + @Path("/marshal/xml") @POST - @Consumes(MediaType.TEXT_PLAIN) + @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) - public Response marshallLastName(String name) throws Exception { - Person p = new Person(); - p.setLastName(name); - - String response = producerTemplate.requestBody("direct:marshal-2", p, String.class); - LOG.infof("Got response from jaxb=>: %s", response); - return Response - .created(new URI("https://camel.apache.org/")) - .entity(response) - .build(); + public Response marshalWithExistingXmlPayload(String xml) { + String response = producerTemplate.requestBody("direct:marshalWithMustBeJAXBElementFalse", xml, String.class); + return Response.ok(response).build(); + } + + @Path("/marshal/part/class") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalPartial( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age, + @QueryParam("useHeader") boolean useHeader) { + + Person person = new PartClassPerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String uri = useHeader ? "direct:marshalPartClassFromHeader" : "direct:marshalPartClass"; + + String response = producerTemplate.requestBody(uri, person, String.class); + return Response.ok(response).build(); + } + + @Path("/unmarshal/part/class") + @POST + @Consumes(MediaType.APPLICATION_XML) + @Produces(MediaType.APPLICATION_JSON) + public Response unmarshalPartial(@QueryParam("useHeader") boolean useHeader, String xml) { + String uri = useHeader ? "direct:unmarshalPartClassFromHeader" : "direct:unmarshalPartClass"; + + Person person = producerTemplate.requestBody(uri, xml, PartClassPerson.class); + return Response.ok(personToJson(person)).build(); + } + + @Path("/unmarshal/ignore/element") + @POST + @Consumes(MediaType.APPLICATION_XML) + @Produces(MediaType.APPLICATION_JSON) + @SuppressWarnings("unchecked") + public Response unmarshalWithIgnoreJaxbElement(String xml) { + JAXBElement<Person> element = producerTemplate.requestBody("direct:unmarshalIgnoreJaxbElement", xml, JAXBElement.class); + Person person = element.getValue(); + return Response.ok(personToJson(person)).build(); + } + + @Path("/marshal/custom/properties") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalWithCustomProperties( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalCustomProperties", person, String.class); + + return Response.ok(xml).build(); + } + + @Path("/marshal/custom/stream/writer") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalWithCustomStreamWriter( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalCustomStreamWriter", person, String.class); + + return Response.ok(xml).build(); + } + + @Path("/marshal/with/object/factory") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalWithObjectFactory( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new FactoryInstantiatedPerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalWithObjectFactory", person, String.class); + + return Response.ok(xml).build(); + } + + @Path("/marshal/without/object/factory") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response marshalWithoutObjectFactory( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new PojoPerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String json = producerTemplate.requestBody("direct:marshalWithoutObjectFactory", person, String.class); + + return Response.ok(json).build(); + } + + @Path("/marshal/non/namespace/schema/location") + @GET + @Produces(MediaType.APPLICATION_XML) + public Response marshalWithNoNamespaceSchemaLocation( + @QueryParam("firstName") String firstName, + @QueryParam("lastName") String lastName, + @QueryParam("age") int age) { + + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(lastName); + person.setAge(age); + + String xml = producerTemplate.requestBody("direct:marshalNoNamespaceSchemaLocation", person, String.class); + + return Response.ok(xml).build(); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbRoute.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbRoute.java index 6c64560d66..bde02e28e8 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbRoute.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/JaxbRoute.java @@ -16,38 +16,121 @@ */ package org.apache.camel.quarkus.component.jaxb.it; -import java.util.Map; - -import javax.xml.bind.JAXBContext; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.inject.Named; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.converter.jaxb.JaxbConstants; import org.apache.camel.converter.jaxb.JaxbDataFormat; -import org.apache.camel.quarkus.component.jaxb.it.model.Person; +import org.apache.camel.quarkus.component.jaxb.it.model.factory.FactoryInstantiatedPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.partial.PartClassPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.simple.SimplePerson; +@ApplicationScoped public class JaxbRoute extends RouteBuilder { - @Override - public void configure() throws Exception { + @Inject + @Named("jaxbDefault") + JaxbDataFormat defaultJaxbDataFormat; - JaxbDataFormat xml = new JaxbDataFormat(); - JAXBContext context = JAXBContext.newInstance(Person.class); - xml.setContext(context); - xml.setNamespacePrefix(Map.of("http://example.com/a", "test")); + @Inject + @Named("jaxbWithNamespacePrefix") + JaxbDataFormat jaxbDataFormatWithNamespacePrefix; - JaxbDataFormat jaxbFromScheme = new JaxbDataFormat(); - jaxbFromScheme.setSchema("classpath:person.xsd"); + @Inject + @Named("jaxbWithEncoding") + JaxbDataFormat jaxbDataFormatWithEncoding; - from("direct:unmarshal") - .unmarshal().jaxb("org.apache.camel.quarkus.component.jaxb.it.model"); + @Inject + @Named("jaxbWithMustBeJAXBElementFalse") + JaxbDataFormat jaxbDataFormatWithMustBeJAXBElementFalse; + + @Inject + @Named("jaxbWithPartClass") + JaxbDataFormat jaxbDataFormatWithPartClass; + + @Inject + @Named("jaxbWithIgnoreElement") + JaxbDataFormat jaxbDataFormatIgnoreElement; + + @Inject + @Named("jaxbWithCustomProperties") + JaxbDataFormat jaxbDataFormatCustomProperties; + + @Inject + @Named("jaxbWithCustomStreamWriter") + JaxbDataFormat jaxbDataFormatCustomStreamWriter; - from("direct:unmarshal-2") - .unmarshal(xml); + @Inject + @Named("jaxbWithoutObjectFactory") + JaxbDataFormat jaxbDataFormatWithoutObjectFactory; + @Inject + @Named("jaxbWithNoNamespaceSchemaLocation") + JaxbDataFormat jaxbDataFormatNoNamespaceSchemaLocation; + + @Override + public void configure() { from("direct:marshal") - .marshal(jaxbFromScheme); + .marshal(defaultJaxbDataFormat); + + from("direct:unmarshal") + .unmarshal(defaultJaxbDataFormat); + + from("direct:marshalJaxbDsl") + .marshal().jaxb(SimplePerson.class.getPackageName()); + + from("direct:unmarshalJaxbDsl") + .unmarshal().jaxb(SimplePerson.class.getPackageName()); + + from("direct:marshalNamespacePrefix") + .marshal(jaxbDataFormatWithNamespacePrefix); + + from("direct:unmarshalNamespacePrefix") + .unmarshal(jaxbDataFormatWithNamespacePrefix); + + from("direct:marshalEncoding") + .marshal(jaxbDataFormatWithEncoding); + + from("direct:unmarshalEncoding") + .unmarshal(jaxbDataFormatWithEncoding); + + from("direct:marshalWithMustBeJAXBElementFalse") + .marshal(jaxbDataFormatWithMustBeJAXBElementFalse); + + from("direct:marshalPartClass") + .marshal(jaxbDataFormatWithPartClass); + + from("direct:marshalPartClassFromHeader") + .setHeader(JaxbConstants.JAXB_PART_CLASS, constant(PartClassPerson.class.getName())) + .setHeader(JaxbConstants.JAXB_PART_NAMESPACE, constant(String.format("{%s}person", PartClassPerson.NAMESPACE))) + .marshal().jaxb(PartClassPerson.class.getPackageName()); + + from("direct:unmarshalPartClass") + .unmarshal(jaxbDataFormatWithPartClass); + + from("direct:unmarshalPartClassFromHeader") + .setHeader(JaxbConstants.JAXB_PART_CLASS, constant(PartClassPerson.class.getName())) + .setHeader(JaxbConstants.JAXB_PART_NAMESPACE, constant(String.format("{%s}person", PartClassPerson.NAMESPACE))) + .unmarshal().jaxb(PartClassPerson.class.getPackageName()); + + from("direct:unmarshalIgnoreJaxbElement") + .unmarshal(jaxbDataFormatIgnoreElement); + + from("direct:marshalCustomProperties") + .marshal(jaxbDataFormatCustomProperties); + + from("direct:marshalCustomStreamWriter") + .marshal(jaxbDataFormatCustomStreamWriter); + + from("direct:marshalWithoutObjectFactory") + .marshal(jaxbDataFormatWithoutObjectFactory); - from("direct:marshal-2") - .marshal(xml); + from("direct:marshalNoNamespaceSchemaLocation") + .marshal(jaxbDataFormatNoNamespaceSchemaLocation); + from("direct:marshalWithObjectFactory") + .marshal().jaxb(FactoryInstantiatedPerson.class.getPackageName()); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/converter/PojoPersonConverter.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/converter/PojoPersonConverter.java new file mode 100644 index 0000000000..cf32fe34f0 --- /dev/null +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/converter/PojoPersonConverter.java @@ -0,0 +1,44 @@ +/* + * 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.quarkus.component.jaxb.it.converter; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import javax.json.Json; + +import org.apache.camel.Converter; +import org.apache.camel.quarkus.component.jaxb.it.model.pojo.PojoPerson; + +import static org.apache.camel.quarkus.component.jaxb.it.JaxbHelper.personToJson; + +/** + * Enables type conversion fallback to {@link InputStream} when JaxbDataFormat.setObjectFactory(false). + */ +@Converter +public class PojoPersonConverter { + + @Converter + public static InputStream toInputStream(PojoPerson person) { + StringWriter writer = new StringWriter(); + Json.createWriter(writer).write(personToJson(person)); + byte[] json = writer.toString().getBytes(StandardCharsets.UTF_8); + return new ByteArrayInputStream(json); + } +} diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java index 24e3567f1a..8af0e06ba5 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java @@ -16,101 +16,16 @@ */ package org.apache.camel.quarkus.component.jaxb.it.model; -import javax.xml.bind.annotation.*; +public interface Person { + String getFirstName(); -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { "firstName", "lastName", "age" }) -@XmlRootElement(name = "person") -public class Person { + void setFirstName(String value); - @XmlElement(required = true, namespace = "http://example.com/a") - protected String firstName = "John"; - @XmlElement(required = true, namespace = "http://example.com/a") - protected String lastName = "Doe"; - @XmlElement(required = true, type = Integer.class, nillable = true) - protected Integer age = 33; + String getLastName(); - /** - * Gets the value of the firstName property. - * - * @return possible object is - * {@link String } - */ - public String getFirstName() { - return firstName; - } + void setLastName(String value); - /** - * Sets the value of the firstName property. - * - * @param value allowed object is - * {@link String } - */ - public void setFirstName(String value) { - this.firstName = value; - } + Integer getAge(); - /** - * Gets the value of the lastName property. - * - * @return possible object is - * {@link String } - */ - public String getLastName() { - return lastName; - } - - /** - * Sets the value of the lastName property. - * - * @param value allowed object is - * {@link String } - */ - public void setLastName(String value) { - this.lastName = value; - } - - /** - * Gets the value of the age property. - * - * @return possible object is - * {@link Integer } - */ - public Integer getAge() { - return age; - } - - /** - * Sets the value of the age property. - * - * @param value allowed object is - * {@link Integer } - */ - public void setAge(Integer value) { - this.age = value; - } - - public Person withFirstName(String value) { - setFirstName(value); - return this; - } - - public Person withLastName(String value) { - setLastName(value); - return this; - } - - public Person withAge(Integer value) { - setAge(value); - return this; - } - - @Override - public String toString() { - return "Person{" + - "firstName='" + firstName + '\'' + - ", lastName='" + lastName + '\'' + - ", age=" + age + - '}'; - } + void setAge(Integer value); } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/factory/FactoryInstantiatedPerson.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/factory/FactoryInstantiatedPerson.java new file mode 100644 index 0000000000..b422f3e7af --- /dev/null +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/factory/FactoryInstantiatedPerson.java @@ -0,0 +1,68 @@ +/* + * 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.quarkus.component.jaxb.it.model.factory; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import org.apache.camel.quarkus.component.jaxb.it.model.Person; + +@RegisterForReflection(fields = false) +public class FactoryInstantiatedPerson implements Person { + public static final String NAMESPACE = "https://example.com/person"; + + protected String firstName; + protected String lastName; + protected Integer age; + + @Override + public String getFirstName() { + return firstName; + } + + @Override + public void setFirstName(String value) { + this.firstName = value; + } + + @Override + public String getLastName() { + return lastName; + } + + @Override + public void setLastName(String value) { + this.lastName = value; + } + + @Override + public Integer getAge() { + return age; + } + + @Override + public void setAge(Integer value) { + this.age = value; + } + + @Override + public String toString() { + return "FactoryInstantiatedPerson{" + + "firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/factory/ObjectFactory.java similarity index 60% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/factory/ObjectFactory.java index 46f83efa48..c5199b3052 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/factory/ObjectFactory.java @@ -14,24 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it.model.factory; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.apache.camel.converter.jaxb.person - */ public ObjectFactory() { } - /** - * Create an instance of {@link Person } - */ - public Person createPerson() { - return new Person(); + public FactoryInstantiatedPerson createFactoryInstantiatedPerson() { + return new FactoryInstantiatedPerson(); + } + + @XmlElementDecl(namespace = "", name = "person") + public JAXBElement<FactoryInstantiatedPerson> createPerson(FactoryInstantiatedPerson value) { + return new JAXBElement<>(new QName(FactoryInstantiatedPerson.NAMESPACE, "person"), FactoryInstantiatedPerson.class, + null, value); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/namespaced/NamespacedPerson.java similarity index 51% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/namespaced/NamespacedPerson.java index 24e3567f1a..8457f36cbf 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/namespaced/NamespacedPerson.java @@ -14,100 +14,62 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it.model.namespaced; -import javax.xml.bind.annotation.*; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.camel.quarkus.component.jaxb.it.model.Person; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "firstName", "lastName", "age" }) @XmlRootElement(name = "person") -public class Person { +public class NamespacedPerson implements Person { - @XmlElement(required = true, namespace = "http://example.com/a") - protected String firstName = "John"; - @XmlElement(required = true, namespace = "http://example.com/a") - protected String lastName = "Doe"; - @XmlElement(required = true, type = Integer.class, nillable = true) - protected Integer age = 33; + public static final String NAMESPACE = "https://example.com/a"; + @XmlElement(required = true, namespace = NAMESPACE) + protected String firstName; + @XmlElement(required = true, namespace = NAMESPACE) + protected String lastName; + @XmlElement(required = true, type = Integer.class, nillable = true, namespace = NAMESPACE) + protected Integer age; - /** - * Gets the value of the firstName property. - * - * @return possible object is - * {@link String } - */ + @Override public String getFirstName() { return firstName; } - /** - * Sets the value of the firstName property. - * - * @param value allowed object is - * {@link String } - */ + @Override public void setFirstName(String value) { this.firstName = value; } - /** - * Gets the value of the lastName property. - * - * @return possible object is - * {@link String } - */ + @Override public String getLastName() { return lastName; } - /** - * Sets the value of the lastName property. - * - * @param value allowed object is - * {@link String } - */ + @Override public void setLastName(String value) { this.lastName = value; } - /** - * Gets the value of the age property. - * - * @return possible object is - * {@link Integer } - */ + @Override public Integer getAge() { return age; } - /** - * Sets the value of the age property. - * - * @param value allowed object is - * {@link Integer } - */ + @Override public void setAge(Integer value) { this.age = value; } - public Person withFirstName(String value) { - setFirstName(value); - return this; - } - - public Person withLastName(String value) { - setLastName(value); - return this; - } - - public Person withAge(Integer value) { - setAge(value); - return this; - } - @Override public String toString() { - return "Person{" + + return "NamespacedPerson{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/namespaced/ObjectFactory.java similarity index 72% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/namespaced/ObjectFactory.java index 46f83efa48..ec0c67a903 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/namespaced/ObjectFactory.java @@ -14,24 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it.model.namespaced; import javax.xml.bind.annotation.XmlRegistry; @XmlRegistry public class ObjectFactory { - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.apache.camel.converter.jaxb.person - */ public ObjectFactory() { } - /** - * Create an instance of {@link Person } - */ - public Person createPerson() { - return new Person(); + public NamespacedPerson createNamespacedPerson() { + return new NamespacedPerson(); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/ObjectFactory.java similarity index 72% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/ObjectFactory.java index 46f83efa48..5c418d2048 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/ObjectFactory.java @@ -14,24 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it.model.partial; import javax.xml.bind.annotation.XmlRegistry; @XmlRegistry public class ObjectFactory { - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.apache.camel.converter.jaxb.person - */ public ObjectFactory() { } - /** - * Create an instance of {@link Person } - */ - public Person createPerson() { - return new Person(); + public PartClassPerson createPartClassPerson() { + return new PartClassPerson(); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/PartClassPerson.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/PartClassPerson.java new file mode 100644 index 0000000000..eaba91b868 --- /dev/null +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/PartClassPerson.java @@ -0,0 +1,77 @@ +/* + * 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.quarkus.component.jaxb.it.model.partial; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.camel.quarkus.component.jaxb.it.model.Person; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "Person") +public class PartClassPerson implements Person { + + public static final String NAMESPACE = "https://example.com/b"; + + @XmlElement(namespace = NAMESPACE) + protected String firstName; + @XmlElement(namespace = NAMESPACE) + protected String lastName; + @XmlElement(namespace = NAMESPACE) + protected Integer age; + + @Override + public String getFirstName() { + return firstName; + } + + @Override + public void setFirstName(String value) { + this.firstName = value; + } + + @Override + public String getLastName() { + return lastName; + } + + @Override + public void setLastName(String value) { + this.lastName = value; + } + + @Override + public Integer getAge() { + return age; + } + + @Override + public void setAge(Integer value) { + this.age = value; + } + + @Override + public String toString() { + return "PartClassPerson{" + + "firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/package-info.java similarity index 61% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/package-info.java index 46f83efa48..be71be0b12 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/partial/package-info.java @@ -14,24 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +@XmlSchema(xmlns = { + @XmlNs(prefix = "person", namespaceURI = PartClassPerson.NAMESPACE) +}) +package org.apache.camel.quarkus.component.jaxb.it.model.partial; -import javax.xml.bind.annotation.XmlRegistry; - -@XmlRegistry -public class ObjectFactory { - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.apache.camel.converter.jaxb.person - */ - public ObjectFactory() { - } - - /** - * Create an instance of {@link Person } - */ - public Person createPerson() { - return new Person(); - } -} +import javax.xml.bind.annotation.XmlNs; +import javax.xml.bind.annotation.XmlSchema; diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/pojo/PojoPerson.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/pojo/PojoPerson.java new file mode 100644 index 0000000000..ed7b950dde --- /dev/null +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/pojo/PojoPerson.java @@ -0,0 +1,65 @@ +/* + * 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.quarkus.component.jaxb.it.model.pojo; + +import org.apache.camel.quarkus.component.jaxb.it.model.Person; + +public class PojoPerson implements Person { + + protected String firstName; + protected String lastName; + protected Integer age; + + @Override + public String getFirstName() { + return firstName; + } + + @Override + public void setFirstName(String value) { + this.firstName = value; + } + + @Override + public String getLastName() { + return lastName; + } + + @Override + public void setLastName(String value) { + this.lastName = value; + } + + @Override + public Integer getAge() { + return age; + } + + @Override + public void setAge(Integer value) { + this.age = value; + } + + @Override + public String toString() { + return "PojoPerson{" + + "firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/simple/ObjectFactory.java similarity index 64% rename from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java rename to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/simple/ObjectFactory.java index 46f83efa48..c70f4cc74b 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/ObjectFactory.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/simple/ObjectFactory.java @@ -14,24 +14,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it.model.simple; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.apache.camel.converter.jaxb.person - */ public ObjectFactory() { } - /** - * Create an instance of {@link Person } - */ - public Person createPerson() { - return new Person(); + public SimplePerson createSimplePerson() { + return new SimplePerson(); + } + + @XmlElementDecl(namespace = "", name = "Person") + public JAXBElement<SimplePerson> createPerson(SimplePerson value) { + return new JAXBElement<>(new QName(SimplePerson.NAMESPACE, "person"), SimplePerson.class, null, value); } } diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/simple/SimplePerson.java similarity index 50% copy from integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java copy to integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/simple/SimplePerson.java index 24e3567f1a..67f3824961 100644 --- a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/Person.java +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/model/simple/SimplePerson.java @@ -14,100 +14,62 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.jaxb.it.model; +package org.apache.camel.quarkus.component.jaxb.it.model.simple; -import javax.xml.bind.annotation.*; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.camel.quarkus.component.jaxb.it.model.Person; @XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { "firstName", "lastName", "age" }) +@XmlType(name = "person", propOrder = { "firstName", "lastName", "age" }) @XmlRootElement(name = "person") -public class Person { +public class SimplePerson implements Person { + public static final String NAMESPACE = "https://example.com/person"; - @XmlElement(required = true, namespace = "http://example.com/a") - protected String firstName = "John"; - @XmlElement(required = true, namespace = "http://example.com/a") - protected String lastName = "Doe"; + @XmlElement(required = true) + protected String firstName; + @XmlElement(required = true) + protected String lastName; @XmlElement(required = true, type = Integer.class, nillable = true) - protected Integer age = 33; + protected Integer age; - /** - * Gets the value of the firstName property. - * - * @return possible object is - * {@link String } - */ + @Override public String getFirstName() { return firstName; } - /** - * Sets the value of the firstName property. - * - * @param value allowed object is - * {@link String } - */ + @Override public void setFirstName(String value) { this.firstName = value; } - /** - * Gets the value of the lastName property. - * - * @return possible object is - * {@link String } - */ + @Override public String getLastName() { return lastName; } - /** - * Sets the value of the lastName property. - * - * @param value allowed object is - * {@link String } - */ + @Override public void setLastName(String value) { this.lastName = value; } - /** - * Gets the value of the age property. - * - * @return possible object is - * {@link Integer } - */ + @Override public Integer getAge() { return age; } - /** - * Sets the value of the age property. - * - * @param value allowed object is - * {@link Integer } - */ + @Override public void setAge(Integer value) { this.age = value; } - public Person withFirstName(String value) { - setFirstName(value); - return this; - } - - public Person withLastName(String value) { - setLastName(value); - return this; - } - - public Person withAge(Integer value) { - setAge(value); - return this; - } - @Override public String toString() { - return "Person{" + + return "SimplePerson{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + diff --git a/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/writer/CustomXmlStreamWriter.java b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/writer/CustomXmlStreamWriter.java new file mode 100644 index 0000000000..fd7d44af73 --- /dev/null +++ b/integration-tests/jaxb/src/main/java/org/apache/camel/quarkus/component/jaxb/it/writer/CustomXmlStreamWriter.java @@ -0,0 +1,199 @@ +/* + * 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.quarkus.component.jaxb.it.writer; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.camel.converter.jaxb.JaxbXmlStreamWriterWrapper; + +/** + * Custom {@link JaxbXmlStreamWriterWrapper} that appends a suffix to all XML tags. + */ +public class CustomXmlStreamWriter implements JaxbXmlStreamWriterWrapper { + private static final String MODIFIED_XML_TAG_SUFFIX = "-modified"; + + @Override + public XMLStreamWriter wrapWriter(XMLStreamWriter writer) { + return new XMLStreamWriter() { + @Override + public void writeStartElement(String localName) throws XMLStreamException { + writer.writeStartElement(localName + MODIFIED_XML_TAG_SUFFIX); + } + + @Override + public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { + writer.writeStartElement(namespaceURI, localName + MODIFIED_XML_TAG_SUFFIX); + + } + + @Override + public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { + writer.writeStartElement(prefix, localName + MODIFIED_XML_TAG_SUFFIX, namespaceURI); + } + + @Override + public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException { + writer.writeEmptyElement(namespaceURI, localName); + } + + @Override + public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { + writer.writeEmptyElement(prefix, localName, namespaceURI); + } + + @Override + public void writeEmptyElement(String localName) throws XMLStreamException { + writer.writeEmptyElement(localName); + } + + @Override + public void writeEndElement() throws XMLStreamException { + writer.writeEndElement(); + } + + @Override + public void writeEndDocument() throws XMLStreamException { + writer.writeEndDocument(); + } + + @Override + public void close() throws XMLStreamException { + writer.close(); + } + + @Override + public void flush() throws XMLStreamException { + writer.flush(); + } + + @Override + public void writeAttribute(String localName, String value) throws XMLStreamException { + writer.writeAttribute(localName, value); + } + + @Override + public void writeAttribute(String prefix, String namespaceURI, String localName, String value) + throws XMLStreamException { + writer.writeAttribute(prefix, namespaceURI, localName, value); + + } + + @Override + public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException { + writer.writeAttribute(namespaceURI, localName, value); + } + + @Override + public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { + writer.writeNamespace(prefix, namespaceURI); + } + + @Override + public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException { + writer.writeDefaultNamespace(namespaceURI); + } + + @Override + public void writeComment(String data) throws XMLStreamException { + writer.writeComment(data); + } + + @Override + public void writeProcessingInstruction(String target) throws XMLStreamException { + writer.writeProcessingInstruction(target); + } + + @Override + public void writeProcessingInstruction(String target, String data) throws XMLStreamException { + writer.writeProcessingInstruction(target, data); + + } + + @Override + public void writeCData(String data) throws XMLStreamException { + writer.writeCData(data); + } + + @Override + public void writeDTD(String dtd) throws XMLStreamException { + writer.writeDTD(dtd); + } + + @Override + public void writeEntityRef(String name) throws XMLStreamException { + writer.writeEntityRef(name); + } + + @Override + public void writeStartDocument() throws XMLStreamException { + writer.writeStartDocument(); + } + + @Override + public void writeStartDocument(String version) throws XMLStreamException { + writer.writeStartDocument(version); + } + + @Override + public void writeStartDocument(String encoding, String version) throws XMLStreamException { + writer.writeStartDocument(encoding, version); + } + + @Override + public void writeCharacters(String text) throws XMLStreamException { + writer.writeCharacters(text); + } + + @Override + public void writeCharacters(char[] text, int start, int len) throws XMLStreamException { + writer.writeCharacters(text, start, len); + } + + @Override + public String getPrefix(String uri) throws XMLStreamException { + return writer.getPrefix(uri); + } + + @Override + public void setPrefix(String prefix, String uri) throws XMLStreamException { + writer.setPrefix(prefix, uri); + } + + @Override + public void setDefaultNamespace(String uri) throws XMLStreamException { + writer.setDefaultNamespace(uri); + } + + @Override + public void setNamespaceContext(NamespaceContext context) throws XMLStreamException { + writer.setNamespaceContext(context); + } + + @Override + public NamespaceContext getNamespaceContext() { + return writer.getNamespaceContext(); + } + + @Override + public Object getProperty(String name) throws IllegalArgumentException { + return writer.getProperty(name); + } + }; + } +} diff --git a/integration-tests/jaxb/src/main/resources/application.properties b/integration-tests/jaxb/src/main/resources/application.properties index ff14981f09..f1efc6a7e8 100644 --- a/integration-tests/jaxb/src/main/resources/application.properties +++ b/integration-tests/jaxb/src/main/resources/application.properties @@ -16,10 +16,7 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -# -# Quarkus -# - -## include xml routes and scheme -quarkus.native.resources.includes = person.xsd - +# Note: jaxb.index for the 'pojo' package is added manually since PojoPerson is a slight edge case test +# where it does not have any JAXB annotations. Thus quarkus-jaxb cannot auto create +# the jaxb.index for the native image +quarkus.native.resources.includes = person.xsd,org/apache/camel/quarkus/component/jaxb/it/model/pojo/jaxb.index diff --git a/integration-tests/jaxb/src/main/resources/application.properties b/integration-tests/jaxb/src/main/resources/org/apache/camel/quarkus/component/jaxb/it/model/pojo/jaxb.index similarity index 90% copy from integration-tests/jaxb/src/main/resources/application.properties copy to integration-tests/jaxb/src/main/resources/org/apache/camel/quarkus/component/jaxb/it/model/pojo/jaxb.index index ff14981f09..ca127c55aa 100644 --- a/integration-tests/jaxb/src/main/resources/application.properties +++ b/integration-tests/jaxb/src/main/resources/org/apache/camel/quarkus/component/jaxb/it/model/pojo/jaxb.index @@ -1,4 +1,3 @@ - ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with @@ -15,11 +14,4 @@ ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- - -# -# Quarkus -# - -## include xml routes and scheme -quarkus.native.resources.includes = person.xsd - +PojoPerson \ No newline at end of file diff --git a/integration-tests/jaxb/src/main/resources/person.xsd b/integration-tests/jaxb/src/main/resources/person.xsd index eb83a4684d..908fe068d9 100644 --- a/integration-tests/jaxb/src/main/resources/person.xsd +++ b/integration-tests/jaxb/src/main/resources/person.xsd @@ -17,12 +17,7 @@ limitations under the License. --> -<xs:schema attributeFormDefault="unqualified" - elementFormDefault="qualified" - xmlns:xs="http://www.w3.org/2001/XMLSchema" - xmlns:a="address.jaxb.converter.camel.apache.org" - targetNamespace="person.jaxb.converter.camel.apache.org"> - +<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="person"> <xs:complexType> <xs:sequence> diff --git a/integration-tests/jaxb/src/test/java/org/apache/camel/quarkus/component/jaxb/it/JaxbTest.java b/integration-tests/jaxb/src/test/java/org/apache/camel/quarkus/component/jaxb/it/JaxbTest.java index 73bfac1993..a136d312ed 100644 --- a/integration-tests/jaxb/src/test/java/org/apache/camel/quarkus/component/jaxb/it/JaxbTest.java +++ b/integration-tests/jaxb/src/test/java/org/apache/camel/quarkus/component/jaxb/it/JaxbTest.java @@ -16,59 +16,390 @@ */ package org.apache.camel.quarkus.component.jaxb.it; +import java.io.StringWriter; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import org.apache.camel.quarkus.component.jaxb.it.model.Person; +import org.apache.camel.quarkus.component.jaxb.it.model.namespaced.NamespacedPerson; +import org.apache.camel.quarkus.component.jaxb.it.model.simple.SimplePerson; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThat; +import static io.restassured.config.XmlConfig.xmlConfig; +import static org.apache.camel.quarkus.component.jaxb.it.model.namespaced.NamespacedPerson.NAMESPACE; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.is; @QuarkusTest class JaxbTest { + private static final String PERSON_FIRST_NAME = "John"; + private static final String PERSON_LAST_NAME = "Doe"; + private static final int PERSON_AGE = 25; + + @Test + public void marshall() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal") + .then() + .statusCode(200) + .body( + "person.firstName", is(PERSON_FIRST_NAME), + "person.lastName", is(PERSON_LAST_NAME), + "person.age", is(String.valueOf(PERSON_AGE))); + } + @Test - public void testUnmarshalLastName() { - String response = RestAssured.given().contentType(ContentType.XML) - .body(getPersonXml("Foo", "Bar", 22)) - .post("/jaxb/unmarshal-lastname") - .then().statusCode(201).extract().asString(); - assertThat(response).isEqualTo("Bar"); + public void unmarshall() { + Person person = new SimplePerson(); + person.setFirstName(PERSON_FIRST_NAME); + person.setLastName(PERSON_LAST_NAME); + person.setAge(PERSON_AGE); + + String xml = getPersonXml(person); + RestAssured.given() + .contentType(ContentType.XML) + .body(xml) + .post("/jaxb/unmarshal") + .then() + .statusCode(200) + .body( + "firstName", is(PERSON_FIRST_NAME), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); } @Test - public void testUnmarshalFirstName() { - String response = RestAssured.given().contentType(ContentType.XML) - .body(getPersonXml("Foo", "Bar", 22)) - .post("/jaxb/unmarshal-firstname") - .then().statusCode(201).extract().asString(); - assertThat(response).isEqualTo("Foo"); + public void marshallWithoutMandatoryField() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal") + .then() + .statusCode(500) + .body("error", containsString("'{lastName}' is expected")); } @Test - public void testMarshallFirstName() { - String name = RestAssured.given().contentType(ContentType.TEXT) - .body("Foo") - .post("/jaxb/marshal-firstname") - .then().statusCode(201).extract().asString(); - assertThat(name).contains("<ns2:firstName>Foo</ns2:firstName>"); + public void unmarshallWithoutMandatoryField() { + Person person = new SimplePerson(); + person.setFirstName(PERSON_FIRST_NAME); + person.setAge(PERSON_AGE); + + String xml = getPersonXml(person); + RestAssured.given() + .contentType(ContentType.XML) + .body(xml) + .post("/jaxb/unmarshal") + .then() + .statusCode(500) + .body("error", containsString("'{lastName}' is expected")); + } + + @Test + public void marshallWithJaxbDsl() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/dsl") + .then() + .statusCode(200) + .body( + "person.firstName", is(PERSON_FIRST_NAME), + "person.lastName", is(PERSON_LAST_NAME), + "person.age", is(String.valueOf(PERSON_AGE))); + } + + @Test + public void unmarshallWithJaxbDsl() { + Person person = new SimplePerson(); + person.setFirstName(PERSON_FIRST_NAME); + person.setLastName(PERSON_LAST_NAME); + person.setAge(PERSON_AGE); + + String xml = getPersonXml(person); + RestAssured.given() + .contentType(ContentType.XML) + .body(xml) + .post("/jaxb/unmarshal/dsl") + .then() + .statusCode(200) + .body( + "firstName", is(PERSON_FIRST_NAME), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); + } + + @Test + public void marshallWithNamespacePrefix() { + RestAssured.given() + .config(RestAssured.config().xmlConfig(xmlConfig().declareNamespace("test", NAMESPACE))) + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/namespace/prefix") + .then() + .statusCode(200) + .body( + "person.firstName", is(PERSON_FIRST_NAME), + "person.lastName", is(PERSON_LAST_NAME), + "person.age", is(String.valueOf(PERSON_AGE))); + } + + @Test + public void unmarshallWithNamespacePrefix() { + Person person = new NamespacedPerson(); + person.setFirstName(PERSON_FIRST_NAME); + person.setLastName(PERSON_LAST_NAME); + person.setAge(PERSON_AGE); + + String xml = getPersonXml(person); + RestAssured.given() + .contentType(ContentType.XML) + .body(xml) + .post("/jaxb/unmarshal/namespace/prefix") + .then() + .statusCode(200) + .body( + "firstName", is(PERSON_FIRST_NAME), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); + } + + @Test + public void marshallWithEncoding() { + String firstName = PERSON_FIRST_NAME.replace("o", "ø"); + // Add some invalid characters that the filterNonXmlChars option will replace with spaces + String lastName = PERSON_LAST_NAME + " \uD83D\uDC33"; + RestAssured.given() + .contentType(ContentType.TEXT) + .queryParam("firstName", firstName) + .queryParam("lastName", lastName) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/encoding") + .then() + .statusCode(200) + .body( + "person.firstName", endsWith(firstName), + "person.lastName", is(PERSON_LAST_NAME + " "), + "person.age", is(String.valueOf(PERSON_AGE))); + } + + @Test + public void unmarshallWithEncoding() { + String firstName = PERSON_FIRST_NAME.replace("o", "ø"); + Person person = new SimplePerson(); + person.setFirstName(firstName); + person.setLastName(PERSON_LAST_NAME); + person.setAge(PERSON_AGE); + + String xml = getPersonXml(person, "ISO-8859-1"); + RestAssured.given() + .contentType(ContentType.XML) + .body(xml) + .post("/jaxb/unmarshal/encoding") + .then() + .statusCode(200) + .body( + "firstName", is(firstName), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); } @Test - public void testMarshallLasttName() { - String name = RestAssured.given().contentType(ContentType.TEXT) - .body("Bar") - .post("/jaxb/marshal-lastname") - .then().statusCode(201).extract().asString(); - assertThat(name).contains("<test:lastName>Bar</test:lastName>"); + public void marshalWithExistingXmlPayload() { + Person person = new SimplePerson(); + person.setFirstName(PERSON_FIRST_NAME); + person.setLastName(PERSON_LAST_NAME); + person.setAge(PERSON_AGE); + + String xml = getPersonXml(person); + RestAssured.given() + .contentType(ContentType.XML) + .body(xml) + .post("/jaxb/marshal/xml") + .then() + .statusCode(200) + .body( + "person.firstName", is(PERSON_FIRST_NAME), + "person.lastName", is(PERSON_LAST_NAME), + "person.age", is(String.valueOf(PERSON_AGE))); + } + + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void marshalWithPartClass(boolean useHeader) { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .queryParam("useHeader", useHeader) + .get("/jaxb/marshal/part/class") + .then() + .statusCode(200) + .body( + "person.firstName", is(PERSON_FIRST_NAME), + "person.lastName", is(PERSON_LAST_NAME), + "person.age", is(String.valueOf(PERSON_AGE))); + } + + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void unmarshalWithPartClass(boolean useHeader) { + String personXml = RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .queryParam("useHeader", useHeader) + .get("/jaxb/marshal/part/class") + .then() + .statusCode(200) + .extract() + .body() + .asString(); + + RestAssured.given() + .queryParam("useHeader", useHeader) + .contentType(ContentType.XML) + .body(personXml) + .post("/jaxb/unmarshal/part/class") + .then() + .statusCode(200) + .body( + "firstName", is(PERSON_FIRST_NAME), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); + } + + @Test + public void unmarshalWithIgnoreJaxbElement() { + String personXml = RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .queryParam("useHeader", false) + .get("/jaxb/marshal/part/class") + .then() + .statusCode(200) + .extract() + .body() + .asString(); + + RestAssured.given() + .contentType(ContentType.XML) + .body(personXml) + .post("/jaxb/unmarshal/ignore/element") + .then() + .statusCode(200) + .body( + "firstName", is(PERSON_FIRST_NAME), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); + } + + @Test + public void marshallWithCustomProperties() { + String[] lines = RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/custom/properties") + .then() + .statusCode(200) + .extract() + .body() + .asString() + .split(System.lineSeparator()); + + // Pretty printing is disabled so we should have only one line + Assertions.assertEquals(1, lines.length); + } + + @Test + public void marshalWithCustomStreamWriter() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/custom/stream/writer") + .then() + .statusCode(200) + .body( + "person-modified.firstName-modified", is(PERSON_FIRST_NAME), + "person-modified.lastName-modified", is(PERSON_LAST_NAME), + "person-modified.age-modified", is(String.valueOf(PERSON_AGE))); + } + + @Test + public void marshalWithObjectFactory() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/with/object/factory") + .then() + .log().body() + .statusCode(200) + .body( + "person.firstName", is(PERSON_FIRST_NAME), + "person.lastName", is(PERSON_LAST_NAME), + "person.age", is(String.valueOf(PERSON_AGE))); + } + + @Test + public void marshalWithoutObjectFactory() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/without/object/factory") + .then() + .statusCode(200) + .body( + "firstName", is(PERSON_FIRST_NAME), + "lastName", is(PERSON_LAST_NAME), + "age", is(PERSON_AGE)); + } + + @Test + public void marshalWithNoNamespaceSchemaLocation() { + RestAssured.given() + .queryParam("firstName", PERSON_FIRST_NAME) + .queryParam("lastName", PERSON_LAST_NAME) + .queryParam("age", PERSON_AGE) + .get("/jaxb/marshal/non/namespace/schema/location") + .then() + .statusCode(200) + .body(containsString("noNamespaceSchemaLocation=\"person-no-namespace.xsd\"")); + } + + private static String getPersonXml(Person person) { + return getPersonXml(person, "UTF-8"); } - private static String getPersonXml(String name, String lastName, Integer age) { - return String.format( - "<person xmlns:ns2=\"http://example.com/a\">" + - "<ns2:firstName>%s</ns2:firstName>" + - "<ns2:lastName>%s</ns2:lastName>" + - "<age>%d</age>" + - "</person>", - name, lastName, age); + private static String getPersonXml(Person person, String encoding) { + try { + StringWriter writer = new StringWriter(); + JAXBContext context = JAXBContext.newInstance(person.getClass()); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding); + marshaller.marshal(person, writer); + return writer.toString(); + } catch (JAXBException e) { + throw new RuntimeException(e); + } } } diff --git a/pom.xml b/pom.xml index 879776b669..83190693b7 100644 --- a/pom.xml +++ b/pom.xml @@ -525,6 +525,7 @@ <ds>SLASHSTAR_STYLE</ds> <groovy>SLASHSTAR_STYLE</groovy> <java>SLASHSTAR_STYLE</java> + <jaxb.index>CAMEL_PROPERTIES_STYLE</jaxb.index> <Jenkinsfile>SLASHSTAR_STYLE</Jenkinsfile> <Jenkinsfile.quarkus>SLASHSTAR_STYLE</Jenkinsfile.quarkus> <properties>CAMEL_PROPERTIES_STYLE</properties>
