From c0c0137a0222d92797c991a7e9755bfecb8bed04 Mon Sep 17 00:00:00 2001 From: Christian Bandowski Date: Wed, 29 May 2019 15:59:36 +0200 Subject: [PATCH] Add simple Quarkus demo application (#59) Added Quarkus demo application that uses MapStruct --- mapstruct-quarkus/README.md | 78 ++++++++ mapstruct-quarkus/pom.xml | 181 ++++++++++++++++++ .../example/quarkus/PersonResource.java | 45 +++++ .../example/quarkus/dto/PersonDto.java | 40 ++++ .../example/quarkus/mapper/PersonMapper.java | 30 +++ .../quarkus/mapper/QuarkusMappingConfig.java | 25 +++ .../example/quarkus/service/Person.java | 37 ++++ .../quarkus/service/PersonService.java | 30 +++ .../quarkus/NativePersonResourceIT.java | 27 +++ .../example/quarkus/PersonResourceTest.java | 40 ++++ pom.xml | 1 + 11 files changed, 534 insertions(+) create mode 100644 mapstruct-quarkus/README.md create mode 100644 mapstruct-quarkus/pom.xml create mode 100644 mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/PersonResource.java create mode 100644 mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/dto/PersonDto.java create mode 100644 mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/PersonMapper.java create mode 100644 mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/QuarkusMappingConfig.java create mode 100644 mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/Person.java create mode 100644 mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/PersonService.java create mode 100644 mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/NativePersonResourceIT.java create mode 100644 mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/PersonResourceTest.java diff --git a/mapstruct-quarkus/README.md b/mapstruct-quarkus/README.md new file mode 100644 index 0000000..b18f44e --- /dev/null +++ b/mapstruct-quarkus/README.md @@ -0,0 +1,78 @@ +# MapStruct + Quarkus + +This example demonstrates how easy it is to use MapStruct in a [Quarkus](https://quarkus.io) application. + +The example was basically built with the following steps: + +1. Create a new Quarkus application + + ``` + mvn io.quarkus:quarkus-maven-plugin:0.15.0:create \ + -DprojectGroupId=org.mapstruct.examples.quarkus \ + -DprojectArtifactId=mapstruct-examples-quarkus \ + -DclassName="org.mapstruct.example.quarkus.PersonResource" \ + -Dpath="/person" \ + -Dextensions="resteasy-jsonb" + ``` + +2. Add the `mapstruct-processor` as a regular `provided` scoped dependency and *not* as described in the +[reference guide](http://mapstruct.org/documentation/dev/reference/html/#_apache_maven) + + ```xml + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + provided + + ``` + +3. Set CDI as the default component-model (see [reference guide](http://mapstruct.org/documentation/dev/reference/html/#configuration-options)) +4. Define a mapper and inject it with `@Inject` in the service + +That's it! + +### Native images + +As MapStruct is not using reflection within the mappers native images are supported as long as CDI is used to retrieve the mappers. +Just try to package your Quarkus application in a native image and feel the speed! + +``` +mvn package -Dnative +``` + +In case you prefer to use `Mappers.getMapper(...)` be aware that this is not automatically supported by native images. Internally the `getMapper` method uses reflection to +retrieve the mapper implementation. +This is the only place where MapStruct uses reflection and thus it causes issues with native images. + +There are some workarounds by defining additional metadata used during native image creation by GraalVM. For example you can create a `Feature` as described +[here](https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md) and register all you mapper implementations and their constructor(s). + +For example if you just have a `FoobarMapper` the feature might look like this: + +```java +@AutomaticFeature +class MapstructMapperFeature implements Feature { + public void beforeAnalysis(BeforeAnalysisAccess access) { + try { + RuntimeReflection.register(FoobarMapperImpl.class); + RuntimeReflection.register(FoobarMapperImpl.class.getConstructors()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } +} +``` + +_(As `FoobarMapperImpl` is a generated class you first have to generate your mappers before you can define this class, otherwise you will get compiliation issues)_ + + +### One drawback + +When using the Quarkus dev mode the mapper will automatically be (re)generated in case you make change to the `@Mapper` annotated class. +Changing a dependent class (like `PersonDto` in this example) will not (re)generate the mapper implementation. + +As a workaround you can quit the dev-mode, recompile and start the application once again or you can make a (temporary) change to the `@Mapper` annotated class so that it +will be picked up and a valid implementation will be generated. + +*Pay attention*: You have to add the mapstruct-processor as described above and *not* using the `maven-compile-plugin`. diff --git a/mapstruct-quarkus/pom.xml b/mapstruct-quarkus/pom.xml new file mode 100644 index 0000000..20046e0 --- /dev/null +++ b/mapstruct-quarkus/pom.xml @@ -0,0 +1,181 @@ + + + + 4.0.0 + org.mapstruct.examples.quarkus + mapstruct-examples-quarkus + 1.0-SNAPSHOT + + + 2.22.0 + 0.15.0 + 1.3.0.Final + UTF-8 + 1.8 + 1.8 + + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + io.quarkus + quarkus-resteasy-jsonb + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + provided + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + quarkus-build + + build + + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + + + + + + + + + native + + + native + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + native-image + + + true + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + + + java12+ + + [12,) + + + true + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + quarkus-build + none + + + + + + + + diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/PersonResource.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/PersonResource.java new file mode 100644 index 0000000..94408e3 --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/PersonResource.java @@ -0,0 +1,45 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.mapstruct.example.quarkus.mapper.PersonMapper; +import org.mapstruct.example.quarkus.dto.PersonDto; +import org.mapstruct.example.quarkus.service.PersonService; + +@Path("/person") +public class PersonResource { + + @Inject + PersonService personService; + + @Inject + PersonMapper personMapper; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public PersonDto loadPerson() { + return personMapper.toResource( personService.loadPerson() ); + } +} \ No newline at end of file diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/dto/PersonDto.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/dto/PersonDto.java new file mode 100644 index 0000000..8993d06 --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/dto/PersonDto.java @@ -0,0 +1,40 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus.dto; + +public class PersonDto { + private String firstname; + private String surname; + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } +} diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/PersonMapper.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/PersonMapper.java new file mode 100644 index 0000000..41fe8d3 --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/PersonMapper.java @@ -0,0 +1,30 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.example.quarkus.dto.PersonDto; +import org.mapstruct.example.quarkus.service.Person; + +@Mapper(config = QuarkusMappingConfig.class) +public interface PersonMapper { + @Mapping(target = "surname", source = "lastname") + PersonDto toResource(Person person); +} diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/QuarkusMappingConfig.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/QuarkusMappingConfig.java new file mode 100644 index 0000000..20797e8 --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/QuarkusMappingConfig.java @@ -0,0 +1,25 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus.mapper; + +import org.mapstruct.MapperConfig; + +@MapperConfig(componentModel = "cdi") +interface QuarkusMappingConfig { +} diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/Person.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/Person.java new file mode 100644 index 0000000..0ee76ab --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/Person.java @@ -0,0 +1,37 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus.service; + +public class Person { + private String firstname; + private String lastname; + + public Person(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } +} diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/PersonService.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/PersonService.java new file mode 100644 index 0000000..45618ff --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/service/PersonService.java @@ -0,0 +1,30 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus.service; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class PersonService { + + public Person loadPerson() { + return new Person("Bob", "Miller"); + } + +} diff --git a/mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/NativePersonResourceIT.java b/mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/NativePersonResourceIT.java new file mode 100644 index 0000000..a184af0 --- /dev/null +++ b/mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/NativePersonResourceIT.java @@ -0,0 +1,27 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus; + +import io.quarkus.test.junit.SubstrateTest; + +@SubstrateTest +public class NativePersonResourceIT extends PersonResourceTest { + + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/PersonResourceTest.java b/mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/PersonResourceTest.java new file mode 100644 index 0000000..2f90b76 --- /dev/null +++ b/mapstruct-quarkus/src/test/java/org/mapstruct/example/quarkus/PersonResourceTest.java @@ -0,0 +1,40 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed 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.mapstruct.example.quarkus; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTest +public class PersonResourceTest { + + @Test + public void testPersonEndpoint() { + given() + .when().get("/person") + .then() + .statusCode(200) + .body( "firstname", is("Bob") ) + .body("surname", is("Miller") ); + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0ec429a..9ec8172 100644 --- a/pom.xml +++ b/pom.xml @@ -45,5 +45,6 @@ mapstruct-suppress-unmapped mapstruct-clone mapstruct-mapper-repo + mapstruct-quarkus