diff --git a/mapstruct-quarkus/README.md b/mapstruct-quarkus/README.md new file mode 100644 index 0000000..577644b --- /dev/null +++ b/mapstruct-quarkus/README.md @@ -0,0 +1,54 @@ +# 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.11.0:create -DprojectGroupId=org.mapstruct.examples.quarkus -DprojectArtifactId=mapstruct-examples-quarkus -DclassName="org.mapstruct.example.quarkus.PersonResource" -Dpath="/person" -Dextensions="resteasy-jsonb" + ``` + +2. Add MapStruct as described in the [reference guide](http://mapstruct.org/documentation/dev/reference/html/#_apache_maven) +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 not automatically be (re)generated. +That means you have to compile (and restart) your application in case you made a change that affects the mapper. diff --git a/mapstruct-quarkus/pom.xml b/mapstruct-quarkus/pom.xml new file mode 100644 index 0000000..5327973 --- /dev/null +++ b/mapstruct-quarkus/pom.xml @@ -0,0 +1,169 @@ + + + + 4.0.0 + org.mapstruct.examples.quarkus + mapstruct-examples-quarkus + 1.0-SNAPSHOT + + + 2.22.0 + 0.11.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 + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-junit5 + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-resteasy-jsonb + + + + + + + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + -Amapstruct.defaultComponentModel=cdi + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + 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 + + + + + + + + + + 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..9fdb21b --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/PersonResource.java @@ -0,0 +1,46 @@ +/** + * 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.resource.PersonDto; +import org.mapstruct.example.quarkus.service.Person; +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/mapper/PersonMapper.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/mapper/PersonMapper.java new file mode 100644 index 0000000..f0e424b --- /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.resource.PersonDto; +import org.mapstruct.example.quarkus.service.Person; + +@Mapper +public interface PersonMapper { + @Mapping( target="surname", source="lastname" ) + PersonDto toResource(Person person); +} diff --git a/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/resource/PersonDto.java b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/resource/PersonDto.java new file mode 100644 index 0000000..bd646a1 --- /dev/null +++ b/mapstruct-quarkus/src/main/java/org/mapstruct/example/quarkus/resource/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.resource; + +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/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 16227f9..f1d2be2 100644 --- a/pom.xml +++ b/pom.xml @@ -44,5 +44,6 @@ mapstruct-metadata-with-annotations mapstruct-suppress-unmapped mapstruct-clone + mapstruct-quarkus