diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 13cd77475..0789e3051 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -156,4 +156,11 @@ public class MavenIntegrationTest { void usesTypeGenerationTestEclipse() { } + /** + * Tests usage of MapStruct with faulty provider of AstModifyingAnnotationProcessor. + */ + @ProcessorTest(baseDir = "faultyAstModifyingAnnotationProcessorTest") + void faultyAstModifyingProcessor() { + } + } diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml new file mode 100644 index 000000000..08390085d --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-aggregator + 1.0.0 + ../pom.xml + + + itest-faultyAstModifyingProcessor-generator + jar + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -proc:none + + + + + + + diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java new file mode 100644 index 000000000..e4a5fe5ea --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java @@ -0,0 +1,21 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class AstModifyingProcessorSaysYes implements AstModifyingAnnotationProcessor { + + @Override + public boolean isTypeComplete(TypeMirror type) { + return true; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.java new file mode 100644 index 000000000..ba1be9b2c --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.java @@ -0,0 +1,25 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class FaultyAstModifyingProcessor implements AstModifyingAnnotationProcessor { + + public FaultyAstModifyingProcessor() { + throw new RuntimeException( "Failed to create processor" ); + } + + @Override + public boolean isTypeComplete(TypeMirror type) { + return true; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java new file mode 100644 index 000000000..5f1616501 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java @@ -0,0 +1,27 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class FaultyStaticAstModifyingProcessor implements AstModifyingAnnotationProcessor { + + static { + if ( true ) { + throw new RuntimeException( "Failed to initialize class" ); + } + } + + @Override + public boolean isTypeComplete(TypeMirror type) { + return true; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor new file mode 100644 index 000000000..3bc4f6138 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor @@ -0,0 +1,7 @@ +# Copyright MapStruct Authors. +# +# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 +org.mapstruct.itest.faultyAstModifyingProcessor.UnknownAstModifyingProcessor +org.mapstruct.itest.faultyAstModifyingProcessor.AstModifyingProcessorSaysYes +org.mapstruct.itest.faultyAstModifyingProcessor.FaultyAstModifyingProcessor +org.mapstruct.itest.faultyAstModifyingProcessor.FaultyStaticAstModifyingProcessor diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml new file mode 100644 index 000000000..dae561252 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml @@ -0,0 +1,27 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-aggregator + pom + + + generator + usage + + diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml new file mode 100644 index 000000000..cc0d0e4f9 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-aggregator + 1.0.0 + ../pom.xml + + + itest-faultyAstModifyingProcessor-usage + jar + + + + junit + junit + 4.12 + test + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-generator + 1.0.0 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -XprintProcessorInfo + -XprintRounds + + -proc:none + + + + + diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java new file mode 100644 index 000000000..1d7cd6380 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +/** + * @author Filip Hrisafov + */ +public class Order { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java new file mode 100644 index 000000000..afc64606b --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +/** + * @author Filip Hrisafov + */ +public class OrderDto { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java new file mode 100644 index 000000000..75e52eb47 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java @@ -0,0 +1,20 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + OrderDto orderToDto(Order order); +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java new file mode 100644 index 000000000..06bfa3c53 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java @@ -0,0 +1,27 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test for using MapStruct with a faulty AstModifyingProcessor (i.e. when the processor cannot be loaded) + * + * @author Filip Hrisafov + */ +public class FaultyAstModifyingTestTest { + + @Test + public void testMapping() { + Order order = new Order(); + order.setItem( "my item" ); + + OrderDto dto = OrderMapper.INSTANCE.orderToDto( order ); + assertThat( dto.getItem() ).isEqualTo( "my item" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 2b6f353d0..8fdc6201e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -5,7 +5,10 @@ */ package org.mapstruct.ap.internal.util; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; @@ -45,7 +48,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen public AnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messager messager, boolean verbose) { astModifyingAnnotationProcessors = java.util.Collections.unmodifiableList( - findAstModifyingAnnotationProcessors() ); + findAstModifyingAnnotationProcessors( messager ) ); this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; @@ -105,20 +108,86 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen this.initialized = true; } - private static List findAstModifyingAnnotationProcessors() { + private static List findAstModifyingAnnotationProcessors(Messager messager) { List processors = new ArrayList<>(); ServiceLoader loader = ServiceLoader.load( AstModifyingAnnotationProcessor.class, AnnotationProcessorContext.class.getClassLoader() ); - for ( AstModifyingAnnotationProcessor astModifyingAnnotationProcessor : loader ) { - processors.add( astModifyingAnnotationProcessor ); + // Lombok packages an AstModifyingAnnotationProcessor as part of their jar + // this leads to problems within Eclipse when lombok is used as an agent + // Therefore we are wrapping this into an iterator that can handle exceptions by ignoring + // the faulty processor + Iterator loaderIterator = new FaultyDelegatingIterator( + messager, + loader.iterator() + ); + + while ( loaderIterator.hasNext() ) { + AstModifyingAnnotationProcessor processor = loaderIterator.next(); + if ( processor != null ) { + processors.add( processor ); + } } return processors; } + private static class FaultyDelegatingIterator implements Iterator { + + private final Messager messager; + private final Iterator delegate; + + private FaultyDelegatingIterator(Messager messager, + Iterator delegate) { + this.messager = messager; + this.delegate = delegate; + } + + @Override + public boolean hasNext() { + // Check the delegate maximum of 5 times + // before returning false + int failures = 5; + while ( failures > 0 ) { + try { + return delegate.hasNext(); + } + catch ( Throwable t ) { + failures--; + logFailure( t ); + } + } + + return false; + } + + @Override + public AstModifyingAnnotationProcessor next() { + try { + return delegate.next(); + } + catch ( Throwable t ) { + logFailure( t ); + return null; + } + } + + private void logFailure(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace( new PrintWriter( sw ) ); + + String reportableStacktrace = sw.toString().replace( System.lineSeparator(), " " ); + + messager.printMessage( + Diagnostic.Kind.WARNING, + "Failed to read AstModifyingAnnotationProcessor. Reading next processor. Reason: " + + reportableStacktrace + ); + } + } + @Override public Elements getElementUtils() { return elementUtils; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.java new file mode 100644 index 000000000..99d88e988 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.java @@ -0,0 +1,25 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._1159; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class FaultyAstModifyingAnnotationProcessor implements AstModifyingAnnotationProcessor { + + public FaultyAstModifyingAnnotationProcessor() { + throw new RuntimeException( "Faulty AstModifyingAnnotationProcessor should not be instantiated" ); + } + + @Override + public boolean isTypeComplete(TypeMirror type) { + return false; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java new file mode 100644 index 000000000..ab1441c14 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java @@ -0,0 +1,52 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._1159; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue1159Mapper { + + Issue1159Mapper INSTANCE = Mappers.getMapper( Issue1159Mapper.class ); + + CarManualDto translateManual(CarManual manual); + + /** + * @author Filip Hrisafov + */ + class CarManual { + + private String content; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } + + /** + * @author Filip Hrisafov + */ + class CarManualDto { + + private String content; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java new file mode 100644 index 000000000..b93cd6d06 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java @@ -0,0 +1,60 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._1159; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.Compiler; +import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("1159") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue1159Mapper.class, +}) +@WithServiceImplementation( + provides = AstModifyingAnnotationProcessor.class, + value = FaultyAstModifyingAnnotationProcessor.class +) +public class Issue1159Test { + + @Test + // The warning is not present in the Eclipse compilation for some reason + @DisabledOnCompiler({ + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + }) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + messageRegExp = "Failed to read AstModifyingAnnotationProcessor. Reading next processor. Reason:.*" + + "Faulty AstModifyingAnnotationProcessor should not be instantiated" + ) + }) + public void shouldIgnoreFaultyAstModifyingProcessor() { + + Issue1159Mapper.CarManual manual = new Issue1159Mapper.CarManual(); + manual.setContent( "test" ); + + Issue1159Mapper.CarManualDto manualDto = Issue1159Mapper.INSTANCE.translateManual( manual ); + + assertThat( manualDto ).isNotNull(); + assertThat( manualDto.getContent() ).isEqualTo( "test" ); + } +}