#1159: Do not fail loading AnnotationProcessorContext if loading AstModifyingAnnotationProcessor fails

This commit is contained in:
Filip Hrisafov 2020-04-04 20:19:43 +02:00
parent c58f80cc5f
commit 3bffe96983
16 changed files with 517 additions and 4 deletions

View File

@ -156,4 +156,11 @@ public class MavenIntegrationTest {
void usesTypeGenerationTestEclipse() {
}
/**
* Tests usage of MapStruct with faulty provider of AstModifyingAnnotationProcessor.
*/
@ProcessorTest(baseDir = "faultyAstModifyingAnnotationProcessorTest")
void faultyAstModifyingProcessor() {
}
}

View File

@ -0,0 +1,50 @@
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct.itest</groupId>
<artifactId>itest-faultyAstModifyingProcessor-aggregator</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>itest-faultyAstModifyingProcessor-generator</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<compilerArg>-proc:none</compilerArg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.mapstruct.itest</groupId>
<artifactId>itest-faultyAstModifyingProcessor-aggregator</artifactId>
<packaging>pom</packaging>
<modules>
<module>generator</module>
<module>usage</module>
</modules>
</project>

View File

@ -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
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct.itest</groupId>
<artifactId>itest-faultyAstModifyingProcessor-aggregator</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>itest-faultyAstModifyingProcessor-usage</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mapstruct.itest</groupId>
<artifactId>itest-faultyAstModifyingProcessor-generator</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<compilerArg>-XprintProcessorInfo</compilerArg>
<compilerArg>-XprintRounds</compilerArg>
</compilerArgs>
<testCompilerArgument>-proc:none</testCompilerArgument>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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" );
}
}

View File

@ -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<AstModifyingAnnotationProcessor> findAstModifyingAnnotationProcessors() {
private static List<AstModifyingAnnotationProcessor> findAstModifyingAnnotationProcessors(Messager messager) {
List<AstModifyingAnnotationProcessor> processors = new ArrayList<>();
ServiceLoader<AstModifyingAnnotationProcessor> 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<AstModifyingAnnotationProcessor> 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<AstModifyingAnnotationProcessor> {
private final Messager messager;
private final Iterator<AstModifyingAnnotationProcessor> delegate;
private FaultyDelegatingIterator(Messager messager,
Iterator<AstModifyingAnnotationProcessor> 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;

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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" );
}
}