mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#825 Add @WithServiceImplementation annotation in testutil to register SPI implementations in processor tests.
This commit is contained in:
parent
1e6b81176d
commit
051177e409
@ -51,6 +51,7 @@
|
||||
<exclude>**/*Erroneous*.java</exclude>
|
||||
<exclude>**/*Test.java</exclude>
|
||||
<exclude>**/testutil/**/*.java</exclude>
|
||||
<exclude>**/spi/**/*.java</exclude>
|
||||
<exclude>${additionalExclude0}</exclude>
|
||||
<exclude>${additionalExclude1}</exclude>
|
||||
<exclude>${additionalExclude2}</exclude>
|
||||
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.test.naming.spi;
|
||||
|
||||
import java.beans.Introspector;
|
||||
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
|
||||
import org.mapstruct.ap.spi.AccessorNamingStrategy;
|
||||
import org.mapstruct.ap.spi.MethodType;
|
||||
|
||||
/**
|
||||
* A custom {@link AccessorNamingStrategy} recognizing getters in the form of {@code property()} and setters in the
|
||||
* form of {@code withProperty(value)}.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
public class CustomAccessorNamingStrategy implements AccessorNamingStrategy {
|
||||
|
||||
@Override
|
||||
public MethodType getMethodType(ExecutableElement method) {
|
||||
if ( isGetterMethod( method ) ) {
|
||||
return MethodType.GETTER;
|
||||
}
|
||||
else if ( isSetterMethod( method ) ) {
|
||||
return MethodType.SETTER;
|
||||
}
|
||||
else if ( isAdderMethod( method ) ) {
|
||||
return MethodType.ADDER;
|
||||
}
|
||||
else {
|
||||
return MethodType.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGetterMethod(ExecutableElement method) {
|
||||
return method.getReturnType().getKind() != TypeKind.VOID;
|
||||
}
|
||||
|
||||
public boolean isSetterMethod(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
|
||||
return methodName.startsWith( "with" ) && methodName.length() > 4;
|
||||
}
|
||||
|
||||
public boolean isAdderMethod(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return methodName.startsWith( "add" ) && methodName.length() > 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName(ExecutableElement getterOrSetterMethod) {
|
||||
String methodName = getterOrSetterMethod.getSimpleName().toString();
|
||||
return Introspector.decapitalize( methodName.startsWith( "with" ) ? methodName.substring( 4 ) : methodName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName(ExecutableElement adderMethod) {
|
||||
String methodName = adderMethod.getSimpleName().toString();
|
||||
return Introspector.decapitalize( methodName.substring( 3 ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectionGetterName(String property) {
|
||||
return property.substring( 0, 1 ).toUpperCase() + property.substring( 1 );
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.test.naming.spi;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.spi.AccessorNamingStrategy;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.WithServiceImplementation;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* Test do demonstrate the usage of custom implementations of {@link AccessorNamingStrategy}.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
@WithClasses({ GolfPlayer.class, GolfPlayerDto.class, GolfPlayerMapper.class })
|
||||
@WithServiceImplementation(CustomAccessorNamingStrategy.class)
|
||||
public class CustomNamingStrategyTest {
|
||||
@Test
|
||||
public void shouldApplyCustomNamingStrategy() {
|
||||
GolfPlayer player = new GolfPlayer()
|
||||
.withName( "Jared" )
|
||||
.withHandicap( 9.2D );
|
||||
|
||||
GolfPlayerDto dto = GolfPlayerMapper.INSTANCE.golfPlayerToDto( player );
|
||||
|
||||
assertThat( dto ).isNotNull();
|
||||
assertThat( dto.name() ).isEqualTo( "Jared" );
|
||||
assertThat( dto.handicap() ).isEqualTo( 9.2D );
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.test.naming.spi;
|
||||
|
||||
/**
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
public class GolfPlayer {
|
||||
|
||||
private double handicap;
|
||||
private String name;
|
||||
|
||||
public double handicap() {
|
||||
return handicap;
|
||||
}
|
||||
|
||||
public GolfPlayer withHandicap(double handicap) {
|
||||
this.handicap = handicap;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public GolfPlayer withName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.test.naming.spi;
|
||||
|
||||
/**
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
public class GolfPlayerDto {
|
||||
|
||||
private double handicap;
|
||||
private String name;
|
||||
|
||||
public double handicap() {
|
||||
return handicap;
|
||||
}
|
||||
|
||||
public void withHandicap(double handicap) {
|
||||
this.handicap = handicap;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void withName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.test.naming.spi;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface GolfPlayerMapper {
|
||||
|
||||
GolfPlayerMapper INSTANCE = Mappers.getMapper( GolfPlayerMapper.class );
|
||||
|
||||
GolfPlayerDto golfPlayerToDto(GolfPlayer player);
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.testutil;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies an implementation of an SPI to be used during the annotation processing.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface WithServiceImplementation {
|
||||
/**
|
||||
* @return The service implementation class that is to be made available during the annotation processing.
|
||||
*/
|
||||
Class<?> value();
|
||||
|
||||
/**
|
||||
* @return The SPI that the service implementation provides. If omitted, then {@link #value()} is expected to
|
||||
* implement exactly one interface which will be considered the provided SPI implementation.
|
||||
*/
|
||||
Class<?> provides() default Object.class;
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.testutil;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies implementations of SPIs to be used during the annotation processing.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface WithServiceImplementations {
|
||||
|
||||
/**
|
||||
* @return The SPI implementations to use.
|
||||
*/
|
||||
WithServiceImplementation[] value();
|
||||
}
|
@ -19,17 +19,20 @@
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a compilation task for a number of sources with given processor options.
|
||||
*/
|
||||
class CompilationRequest {
|
||||
public class CompilationRequest {
|
||||
private final Set<Class<?>> sourceClasses;
|
||||
private final Map<Class<?>, Class<?>> services;
|
||||
private final List<String> processorOptions;
|
||||
|
||||
CompilationRequest(Set<Class<?>> sourceClasses, List<String> processorOptions) {
|
||||
CompilationRequest(Set<Class<?>> sourceClasses, Map<Class<?>, Class<?>> services, List<String> processorOptions) {
|
||||
this.sourceClasses = sourceClasses;
|
||||
this.services = services;
|
||||
this.processorOptions = processorOptions;
|
||||
}
|
||||
|
||||
@ -38,6 +41,7 @@ class CompilationRequest {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( processorOptions == null ) ? 0 : processorOptions.hashCode() );
|
||||
result = prime * result + ( ( services == null ) ? 0 : services.hashCode() );
|
||||
result = prime * result + ( ( sourceClasses == null ) ? 0 : sourceClasses.hashCode() );
|
||||
return result;
|
||||
}
|
||||
@ -55,7 +59,9 @@ class CompilationRequest {
|
||||
}
|
||||
CompilationRequest other = (CompilationRequest) obj;
|
||||
|
||||
return processorOptions.equals( other.processorOptions ) && sourceClasses.equals( other.sourceClasses );
|
||||
return processorOptions.equals( other.processorOptions )
|
||||
&& services.equals( other.services )
|
||||
&& sourceClasses.equals( other.sourceClasses );
|
||||
}
|
||||
|
||||
public Set<Class<?>> getSourceClasses() {
|
||||
@ -65,4 +71,8 @@ class CompilationRequest {
|
||||
public List<String> getProcessorOptions() {
|
||||
return processorOptions;
|
||||
}
|
||||
|
||||
public Map<Class<?>, Class<?>> getServices() {
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
@ -22,21 +22,26 @@ import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.WithServiceImplementation;
|
||||
import org.mapstruct.ap.testutil.WithServiceImplementations;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
|
||||
@ -67,7 +72,9 @@ abstract class CompilingStatement extends Statement {
|
||||
|
||||
protected static final String SOURCE_DIR = getBasePath() + "/src/test/java";
|
||||
|
||||
protected static final List<String> COMPILER_CLASSPATH = buildCompilerClasspath();
|
||||
protected static final List<String> TEST_COMPILATION_CLASSPATH = buildTestCompilationClasspath();
|
||||
|
||||
protected static final List<String> PROCESSOR_CLASSPATH = buildProcessorClasspath();
|
||||
|
||||
private final FrameworkMethod method;
|
||||
private final CompilationCache compilationCache;
|
||||
@ -75,13 +82,14 @@ abstract class CompilingStatement extends Statement {
|
||||
|
||||
private String classOutputDir;
|
||||
private String sourceOutputDir;
|
||||
private String additionalCompilerClasspath;
|
||||
private CompilationRequest compilationRequest;
|
||||
|
||||
CompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
this.method = method;
|
||||
this.compilationCache = compilationCache;
|
||||
|
||||
this.compilationRequest = new CompilationRequest( getTestClasses(), getProcessorOptions() );
|
||||
this.compilationRequest = new CompilationRequest( getTestClasses(), getServices(), getProcessorOptions() );
|
||||
}
|
||||
|
||||
void setNextStatement(Statement next) {
|
||||
@ -110,32 +118,48 @@ abstract class CompilingStatement extends Statement {
|
||||
|
||||
classOutputDir = compilationRoot + "/classes";
|
||||
sourceOutputDir = compilationRoot + "/generated-sources";
|
||||
additionalCompilerClasspath = compilationRoot + "/compiler";
|
||||
|
||||
createOutputDirs();
|
||||
|
||||
( (ModifiableURLClassLoader) Thread.currentThread().getContextClassLoader() ).addOutputDir( classOutputDir );
|
||||
( (ModifiableURLClassLoader) Thread.currentThread().getContextClassLoader() ).withPath( classOutputDir );
|
||||
}
|
||||
|
||||
protected abstract String getPathSuffix();
|
||||
|
||||
private static List<String> buildCompilerClasspath() {
|
||||
String[] bootClasspath =
|
||||
System.getProperty( "java.class.path" ).split( System.getProperty( "path.separator" ) );
|
||||
String fs = System.getProperty( "file.separator" );
|
||||
String testClasses = "target" + fs + "test-classes";
|
||||
|
||||
private static List<String> buildTestCompilationClasspath() {
|
||||
String[] whitelist =
|
||||
new String[] {
|
||||
"processor" + fs + "target", // the processor itself
|
||||
"core" + fs + "target", // MapStruct annotations in multi-module reactor build or IDE
|
||||
"org" + fs + "mapstruct" + fs + "mapstruct" + fs, // MapStruct annotations in single module build
|
||||
"freemarker",
|
||||
// MapStruct annotations in multi-module reactor build or IDE
|
||||
"core" + File.separator + "target",
|
||||
// MapStruct annotations in single module build
|
||||
"org" + File.separator + "mapstruct" + File.separator + "mapstruct" + File.separator,
|
||||
"guava",
|
||||
"javax.inject",
|
||||
"spring-beans",
|
||||
"spring-context",
|
||||
"joda-time" };
|
||||
|
||||
return filterBootClassPath( whitelist );
|
||||
}
|
||||
|
||||
private static List<String> buildProcessorClasspath() {
|
||||
String[] whitelist =
|
||||
new String[] {
|
||||
"processor" + File.separator + "target", // the processor itself,
|
||||
"freemarker",
|
||||
"javax.inject",
|
||||
"spring-context",
|
||||
"joda-time" };
|
||||
|
||||
return filterBootClassPath( whitelist );
|
||||
}
|
||||
|
||||
protected static List<String> filterBootClassPath(String[] whitelist) {
|
||||
String[] bootClasspath =
|
||||
System.getProperty( "java.class.path" ).split( File.pathSeparator );
|
||||
String testClasses = "target" + File.separator + "test-classes";
|
||||
|
||||
List<String> classpath = new ArrayList<String>();
|
||||
for ( String path : bootClasspath ) {
|
||||
if ( !path.contains( testClasses ) && isWhitelisted( path, whitelist ) ) {
|
||||
@ -303,6 +327,55 @@ abstract class CompilingStatement extends Statement {
|
||||
return testClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resources to be compiled for this test.
|
||||
*
|
||||
* @return A map containing the package were to look for a resource (key) and the resource (value) to be compiled
|
||||
* for this test
|
||||
*/
|
||||
private Map<Class<?>, Class<?>> getServices() {
|
||||
Map<Class<?>, Class<?>> services = new HashMap<Class<?>, Class<?>>();
|
||||
|
||||
addServices( services, method.getAnnotation( WithServiceImplementations.class ) );
|
||||
addService( services, method.getAnnotation( WithServiceImplementation.class ) );
|
||||
|
||||
Class<?> declaringClass = method.getMethod().getDeclaringClass();
|
||||
addServices( services, declaringClass.getAnnotation( WithServiceImplementations.class ) );
|
||||
addService( services, declaringClass.getAnnotation( WithServiceImplementation.class ) );
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private void addServices(Map<Class<?>, Class<?>> services, WithServiceImplementations withImplementations) {
|
||||
if ( withImplementations != null ) {
|
||||
for ( WithServiceImplementation resource : withImplementations.value() ) {
|
||||
addService( services, resource );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addService(Map<Class<?>, Class<?>> services, WithServiceImplementation annoation) {
|
||||
if ( annoation == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> provides = annoation.provides();
|
||||
Class<?> implementor = annoation.value();
|
||||
if ( provides == Object.class ) {
|
||||
Class<?>[] implemented = implementor.getInterfaces();
|
||||
if ( implemented.length != 1 ) {
|
||||
throw new IllegalArgumentException(
|
||||
"The class " + implementor.getName()
|
||||
+ " either needs to implement exactly one interface, or \"provides\" needs to be specified"
|
||||
+ " as well in the annotation " + WithServiceImplementation.class.getSimpleName() + "." );
|
||||
}
|
||||
|
||||
provides = implemented[0];
|
||||
}
|
||||
|
||||
services.put( provides, implementor );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the processor options to be used this test.
|
||||
*
|
||||
@ -344,7 +417,7 @@ abstract class CompilingStatement extends Statement {
|
||||
return String.format( "-A%s=%s", processorOption.name(), processorOption.value() );
|
||||
}
|
||||
|
||||
protected Set<File> getSourceFiles(Collection<Class<?>> classes) {
|
||||
protected static Set<File> getSourceFiles(Collection<Class<?>> classes) {
|
||||
Set<File> sourceFiles = new HashSet<File>( classes.size() );
|
||||
|
||||
for ( Class<?> clazz : classes ) {
|
||||
@ -369,17 +442,33 @@ abstract class CompilingStatement extends Statement {
|
||||
setupDirectories();
|
||||
compilationCache.setLastSourceOutputDir( sourceOutputDir );
|
||||
|
||||
CompilationOutcomeDescriptor resultHolder =
|
||||
compileWithSpecificCompiler( compilationRequest, sourceOutputDir, classOutputDir );
|
||||
boolean needsAdditionalCompilerClasspath = prepareServices();
|
||||
CompilationOutcomeDescriptor resultHolder;
|
||||
|
||||
resultHolder = compileWithSpecificCompiler(
|
||||
compilationRequest,
|
||||
sourceOutputDir,
|
||||
classOutputDir,
|
||||
needsAdditionalCompilerClasspath ? additionalCompilerClasspath : null );
|
||||
|
||||
compilationCache.update( compilationRequest, resultHolder );
|
||||
return resultHolder;
|
||||
}
|
||||
|
||||
protected Object loadAndInstantiate(ClassLoader processorClassloader, Class<?> clazz) {
|
||||
try {
|
||||
return processorClassloader.loadClass( clazz.getName() ).newInstance();
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract CompilationOutcomeDescriptor compileWithSpecificCompiler(
|
||||
CompilationRequest compilationRequest,
|
||||
String sourceOutputDir,
|
||||
String classOutputDir);
|
||||
String classOutputDir,
|
||||
String additionalCompilerClasspath);
|
||||
|
||||
boolean needsRecompilation() {
|
||||
return !compilationRequest.equals( compilationCache.getLastRequest() );
|
||||
@ -402,6 +491,10 @@ abstract class CompilingStatement extends Statement {
|
||||
directory = new File( sourceOutputDir );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
|
||||
directory = new File( additionalCompilerClasspath );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
}
|
||||
|
||||
private void deleteDirectory(File path) {
|
||||
@ -419,6 +512,32 @@ abstract class CompilingStatement extends Statement {
|
||||
path.delete();
|
||||
}
|
||||
|
||||
private boolean prepareServices() {
|
||||
if ( !compilationRequest.getServices().isEmpty() ) {
|
||||
String servicesDir =
|
||||
additionalCompilerClasspath + File.separator + "META-INF" + File.separator + "services";
|
||||
File directory = new File( servicesDir );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
for ( Map.Entry<Class<?>, Class<?>> serviceEntry : compilationRequest.getServices().entrySet() ) {
|
||||
try {
|
||||
File file = new File( servicesDir + File.separator + serviceEntry.getKey().getName() );
|
||||
FileWriter fileWriter = new FileWriter( file );
|
||||
fileWriter.append( serviceEntry.getValue().getName() ).append( "\n" );
|
||||
fileWriter.flush();
|
||||
fileWriter.close();
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class DiagnosticDescriptorComparator implements Comparator<DiagnosticDescriptor> {
|
||||
|
||||
@Override
|
||||
|
@ -19,6 +19,8 @@
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.codehaus.plexus.compiler.CompilerConfiguration;
|
||||
import org.codehaus.plexus.compiler.CompilerException;
|
||||
@ -36,6 +38,14 @@ import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
|
||||
*/
|
||||
class EclipseCompilingStatement extends CompilingStatement {
|
||||
|
||||
private static final List<String> ECLIPSE_COMPILER_CLASSPATH = buildEclipseCompilerClasspath();
|
||||
|
||||
private static final ClassLoader DEFAULT_ECLIPSE_COMPILER_CLASSLOADER =
|
||||
new ModifiableURLClassLoader( newFilteringClassLoaderForEclipse() )
|
||||
.withPaths( ECLIPSE_COMPILER_CLASSPATH )
|
||||
.withPaths( PROCESSOR_CLASSPATH )
|
||||
.withOriginOf( ClassLoaderExecutor.class );
|
||||
|
||||
EclipseCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
super( method, compilationCache );
|
||||
}
|
||||
@ -43,36 +53,105 @@ class EclipseCompilingStatement extends CompilingStatement {
|
||||
@Override
|
||||
protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRequest compilationRequest,
|
||||
String sourceOutputDir,
|
||||
String classOutputDir) {
|
||||
JDTCompiler compiler = new JDTCompiler();
|
||||
compiler.enableLogging( new ConsoleLogger( 5, "JDT-Compiler" ) );
|
||||
String classOutputDir,
|
||||
String additionalCompilerClasspath) {
|
||||
ClassLoader compilerClassloader;
|
||||
if ( additionalCompilerClasspath == null ) {
|
||||
compilerClassloader = DEFAULT_ECLIPSE_COMPILER_CLASSLOADER;
|
||||
}
|
||||
else {
|
||||
ModifiableURLClassLoader loader = new ModifiableURLClassLoader(
|
||||
newFilteringClassLoaderForEclipse()
|
||||
.hidingClasses( compilationRequest.getServices().values() ) );
|
||||
|
||||
CompilerConfiguration config = new CompilerConfiguration();
|
||||
|
||||
config.setClasspathEntries( COMPILER_CLASSPATH );
|
||||
config.setOutputLocation( classOutputDir );
|
||||
config.setGeneratedSourcesDirectory( new File( sourceOutputDir ) );
|
||||
config.setAnnotationProcessors( new String[] { MappingProcessor.class.getName() } );
|
||||
config.setSourceFiles( getSourceFiles( compilationRequest.getSourceClasses() ) );
|
||||
config.setShowWarnings( false );
|
||||
config.setSourceVersion( "1.8" );
|
||||
config.setTargetVersion( "1.8" );
|
||||
|
||||
for ( String option : compilationRequest.getProcessorOptions() ) {
|
||||
config.addCompilerCustomArgument( option, null );
|
||||
compilerClassloader = loader.withPaths( ECLIPSE_COMPILER_CLASSPATH )
|
||||
.withPaths( PROCESSOR_CLASSPATH )
|
||||
.withOriginOf( ClassLoaderExecutor.class )
|
||||
.withPath( additionalCompilerClasspath )
|
||||
.withOriginsOf( compilationRequest.getServices().values() );
|
||||
}
|
||||
|
||||
CompilerResult compilerResult;
|
||||
try {
|
||||
compilerResult = compiler.performCompile( config );
|
||||
}
|
||||
catch ( CompilerException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
ClassLoaderHelper clHelper =
|
||||
(ClassLoaderHelper) loadAndInstantiate( compilerClassloader, ClassLoaderExecutor.class );
|
||||
|
||||
return CompilationOutcomeDescriptor.forResult(
|
||||
return clHelper.compileInOtherClassloader(
|
||||
compilationRequest,
|
||||
TEST_COMPILATION_CLASSPATH,
|
||||
getSourceFiles( compilationRequest.getSourceClasses() ),
|
||||
SOURCE_DIR,
|
||||
compilerResult );
|
||||
sourceOutputDir,
|
||||
classOutputDir );
|
||||
}
|
||||
|
||||
private static FilteringParentClassLoader newFilteringClassLoaderForEclipse() {
|
||||
return new FilteringParentClassLoader(
|
||||
// reload eclipse compiler classes
|
||||
"org.eclipse.",
|
||||
// reload mapstruct processor classes
|
||||
"org.mapstruct.ap.internal.",
|
||||
"org.mapstruct.ap.spi.",
|
||||
"org.mapstruct.ap.MappingProcessor")
|
||||
.hidingClass( ClassLoaderExecutor.class );
|
||||
}
|
||||
|
||||
public interface ClassLoaderHelper {
|
||||
CompilationOutcomeDescriptor compileInOtherClassloader(CompilationRequest compilationRequest,
|
||||
List<String> testCompilationClasspath,
|
||||
Set<File> sourceFiles,
|
||||
String sourceDir,
|
||||
String sourceOutputDir,
|
||||
String classOutputDir);
|
||||
}
|
||||
|
||||
public static final class ClassLoaderExecutor implements ClassLoaderHelper {
|
||||
@Override
|
||||
public CompilationOutcomeDescriptor compileInOtherClassloader(CompilationRequest compilationRequest,
|
||||
List<String> testCompilationClasspath,
|
||||
Set<File> sourceFiles,
|
||||
String sourceDir,
|
||||
String sourceOutputDir,
|
||||
String classOutputDir) {
|
||||
JDTCompiler compiler = new JDTCompiler();
|
||||
compiler.enableLogging( new ConsoleLogger( 5, "JDT-Compiler" ) );
|
||||
|
||||
CompilerConfiguration config = new CompilerConfiguration();
|
||||
|
||||
config.setClasspathEntries( testCompilationClasspath );
|
||||
config.setOutputLocation( classOutputDir );
|
||||
config.setGeneratedSourcesDirectory( new File( sourceOutputDir ) );
|
||||
config.setAnnotationProcessors( new String[] { MappingProcessor.class.getName() } );
|
||||
config.setSourceFiles( sourceFiles );
|
||||
config.setShowWarnings( false );
|
||||
config.setSourceVersion( "1.8" );
|
||||
config.setTargetVersion( "1.8" );
|
||||
|
||||
for ( String option : compilationRequest.getProcessorOptions() ) {
|
||||
config.addCompilerCustomArgument( option, null );
|
||||
}
|
||||
|
||||
CompilerResult compilerResult;
|
||||
try {
|
||||
compilerResult = compiler.performCompile( config );
|
||||
}
|
||||
catch ( CompilerException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
return CompilationOutcomeDescriptor.forResult(
|
||||
sourceDir,
|
||||
compilerResult );
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> buildEclipseCompilerClasspath() {
|
||||
String[] whitelist =
|
||||
new String[] {
|
||||
"tycho-compiler",
|
||||
"org.eclipse.jdt.",
|
||||
"plexus-compiler-api",
|
||||
"plexus-component-annotations" };
|
||||
|
||||
return filterBootClassPath( whitelist );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright 2012-2016 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.ap.testutil.runner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A ClassLoader that can be used to hide packages or classes from its default parent ClassLoader so that the
|
||||
* ClassLoader using this as its parent can load the class on its own.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
final class FilteringParentClassLoader extends ClassLoader {
|
||||
private Collection<String> excludedPrefixes;
|
||||
|
||||
/**
|
||||
* @param excludedPrefixes class name prefixes to exclude
|
||||
*/
|
||||
FilteringParentClassLoader(String... excludedPrefixes) {
|
||||
this.excludedPrefixes = new ArrayList<String>( Arrays.asList( excludedPrefixes ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classes The classes to hide (inner classes are hidden as well)
|
||||
* @return {@code this}
|
||||
*/
|
||||
FilteringParentClassLoader hidingClasses(Collection<Class<?>> classes) {
|
||||
for ( Class<?> clazz : classes ) {
|
||||
hidingClass( clazz );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz The class to hide (inner classes are hidden as well)
|
||||
* @return {@code this}
|
||||
*/
|
||||
FilteringParentClassLoader hidingClass(Class<?> clazz) {
|
||||
excludedPrefixes.add( clazz.getName() );
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
for ( String excluded : excludedPrefixes ) {
|
||||
if ( name.startsWith( excluded ) ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return super.loadClass( name, resolve );
|
||||
}
|
||||
}
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
@ -70,13 +68,7 @@ class InnerAnnotationProcessorRunner extends BlockJUnit4ClassRunner {
|
||||
}
|
||||
|
||||
private static void replaceContextClassLoader(Class<?> klass) {
|
||||
String classFileName = klass.getName().replace( ".", "/" ) + ".class";
|
||||
URL classResource = klass.getClassLoader().getResource( classFileName );
|
||||
String fullyQualifiedUrl = classResource.toExternalForm();
|
||||
String basePath = fullyQualifiedUrl.substring( 0, fullyQualifiedUrl.length() - classFileName.length() );
|
||||
|
||||
ModifiableURLClassLoader testClassLoader = new ModifiableURLClassLoader();
|
||||
testClassLoader.addURL( basePath );
|
||||
ModifiableURLClassLoader testClassLoader = new ModifiableURLClassLoader().withOriginOf( klass );
|
||||
|
||||
Thread.currentThread().setContextClassLoader( testClassLoader );
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
@ -45,7 +46,11 @@ import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
|
||||
*/
|
||||
class JdkCompilingStatement extends CompilingStatement {
|
||||
|
||||
private static final List<File> COMPILER_CLASSPATH_FILES = asFiles( COMPILER_CLASSPATH );
|
||||
private static final List<File> COMPILER_CLASSPATH_FILES = asFiles( TEST_COMPILATION_CLASSPATH );
|
||||
|
||||
private static final ClassLoader DEFAULT_PROCESSOR_CLASSLOADER =
|
||||
new ModifiableURLClassLoader( new FilteringParentClassLoader( "org.mapstruct." ) )
|
||||
.withPaths( PROCESSOR_CLASSPATH );
|
||||
|
||||
JdkCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
super( method, compilationCache );
|
||||
@ -54,7 +59,8 @@ class JdkCompilingStatement extends CompilingStatement {
|
||||
@Override
|
||||
protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRequest compilationRequest,
|
||||
String sourceOutputDir,
|
||||
String classOutputDir) {
|
||||
String classOutputDir,
|
||||
String additionalCompilerClasspath) {
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
|
||||
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
||||
@ -71,6 +77,18 @@ class JdkCompilingStatement extends CompilingStatement {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
ClassLoader processorClassloader;
|
||||
if ( additionalCompilerClasspath == null ) {
|
||||
processorClassloader = DEFAULT_PROCESSOR_CLASSLOADER;
|
||||
}
|
||||
else {
|
||||
processorClassloader = new ModifiableURLClassLoader(
|
||||
new FilteringParentClassLoader( "org.mapstruct." ) )
|
||||
.withPaths( PROCESSOR_CLASSPATH )
|
||||
.withPath( additionalCompilerClasspath )
|
||||
.withOriginsOf( compilationRequest.getServices().values() );
|
||||
}
|
||||
|
||||
CompilationTask task =
|
||||
compiler.getTask(
|
||||
null,
|
||||
@ -80,9 +98,10 @@ class JdkCompilingStatement extends CompilingStatement {
|
||||
null,
|
||||
compilationUnits );
|
||||
|
||||
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
|
||||
task.setProcessors(
|
||||
Arrays.asList( (Processor) loadAndInstantiate( processorClassloader, MappingProcessor.class ) ) );
|
||||
|
||||
Boolean compilationSuccessful = task.call();
|
||||
boolean compilationSuccessful = task.call();
|
||||
|
||||
return CompilationOutcomeDescriptor.forResult(
|
||||
SOURCE_DIR,
|
||||
|
@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@ -32,11 +33,11 @@ import java.util.concurrent.ConcurrentMap;
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
final class ModifiableURLClassLoader extends URLClassLoader {
|
||||
|
||||
private static final String ORG_MAPSTRUCT_AP_TEST = "org.mapstruct.ap.test.";
|
||||
|
||||
private static final FilteringParentClassLoader PARENT_CLASS_LOADER = new FilteringParentClassLoader(
|
||||
private static final FilteringParentClassLoader DEFAULT_PARENT_CLASS_LOADER = new FilteringParentClassLoader(
|
||||
ORG_MAPSTRUCT_AP_TEST );
|
||||
|
||||
static {
|
||||
@ -45,8 +46,12 @@ public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
|
||||
private final ConcurrentMap<URL, URL> addedURLs = new ConcurrentHashMap<URL, URL>();
|
||||
|
||||
public ModifiableURLClassLoader() {
|
||||
super( new URL[] { }, PARENT_CLASS_LOADER );
|
||||
ModifiableURLClassLoader(ClassLoader parent) {
|
||||
super( new URL[] { }, parent );
|
||||
}
|
||||
|
||||
ModifiableURLClassLoader() {
|
||||
this( DEFAULT_PARENT_CLASS_LOADER );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,22 +61,51 @@ public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public void addURL(String basePath) {
|
||||
try {
|
||||
addURL( new URL( basePath ) );
|
||||
}
|
||||
catch ( MalformedURLException e ) {
|
||||
throw new RuntimeException( e );
|
||||
ModifiableURLClassLoader withOriginsOf(Collection<Class<?>> classes) {
|
||||
for ( Class<?> c : classes ) {
|
||||
withOriginOf( c );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void addOutputDir(String outputDir) {
|
||||
ModifiableURLClassLoader withOriginOf(Class<?> clazz) {
|
||||
String classFileName = clazz.getName().replace( ".", "/" ) + ".class";
|
||||
URL classResource = clazz.getClassLoader().getResource( classFileName );
|
||||
String fullyQualifiedUrl = classResource.toExternalForm();
|
||||
String basePath = fullyQualifiedUrl.substring( 0, fullyQualifiedUrl.length() - classFileName.length() );
|
||||
|
||||
return withURL( basePath );
|
||||
}
|
||||
|
||||
ModifiableURLClassLoader withPaths(Collection<String> baseUrls) {
|
||||
for ( String url : baseUrls ) {
|
||||
withPath( url );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ModifiableURLClassLoader withURL(String baseUrl) {
|
||||
try {
|
||||
addURL( new File( outputDir ).toURI().toURL() );
|
||||
addURL( new URL( baseUrl ) );
|
||||
}
|
||||
catch ( MalformedURLException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ModifiableURLClassLoader withPath(String path) {
|
||||
try {
|
||||
addURL( new File( path ).toURI().toURL() );
|
||||
}
|
||||
catch ( MalformedURLException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void tryRegisterAsParallelCapable() {
|
||||
@ -94,21 +128,4 @@ public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
return; // ignore
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FilteringParentClassLoader extends ClassLoader {
|
||||
private String excludedPackage;
|
||||
|
||||
FilteringParentClassLoader(String excludedPackage) {
|
||||
this.excludedPackage = excludedPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if ( name.startsWith( excludedPackage ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.loadClass( name, resolve );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user