DATACMNS-993 - Clean up class loader hacking in tests.
ClassLoaderRule with @ClassLoaderConfiguration allows easy creation FilteringClassLoader. Changed usage pattern of ClassUtils.isPresent(…) in order to ease testing. Copied the ShadowingClassloader from SpringFramework to get a constructor that doesn't shadow anything by default. Original pull request: #202.
This commit is contained in:
committed by
Oliver Gierke
parent
562b6665a3
commit
65734739bd
2
pom.xml
2
pom.xml
@@ -270,7 +270,7 @@
|
||||
<goal>test-process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/generated-sources/test-annotations</outputDirectory>
|
||||
<outputDirectory>${project.build.directory}/generated-test-sources/test-annotations</outputDirectory>
|
||||
<processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
@@ -78,15 +78,22 @@ public @interface EnableSpringDataWebSupport {
|
||||
* https://jira.springsource.org/browse/SPR-10565).
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
static class SpringDataWebConfigurationImportSelector implements ImportSelector, ResourceLoaderAware {
|
||||
|
||||
// Don't make final to allow test cases faking this to false
|
||||
private static boolean HATEOAS_PRESENT = ClassUtils.isPresent("org.springframework.hateoas.Link", null);
|
||||
private static boolean JACKSON_PRESENT = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null);
|
||||
|
||||
private Environment environment;
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.springframework.core.env.Environment)
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader)
|
||||
@@ -105,10 +112,11 @@ public @interface EnableSpringDataWebSupport {
|
||||
|
||||
List<String> imports = new ArrayList<>();
|
||||
|
||||
imports.add(HATEOAS_PRESENT ? HateoasAwareSpringDataWebConfiguration.class.getName()
|
||||
imports.add(ClassUtils.isPresent("org.springframework.hateoas.Link", resourceLoader.getClassLoader())
|
||||
? HateoasAwareSpringDataWebConfiguration.class.getName()
|
||||
: SpringDataWebConfiguration.class.getName());
|
||||
|
||||
if (JACKSON_PRESENT) {
|
||||
if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", resourceLoader.getClassLoader())) {
|
||||
imports.addAll(
|
||||
SpringFactoriesLoader.loadFactoryNames(SpringDataJacksonModules.class, resourceLoader.getClassLoader()));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.classloadersupport;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* configures the {@link ClassLoaderRule}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ClassLoaderConfiguration {
|
||||
|
||||
/**
|
||||
* classes of which the package that contains them will be loaded by the {@link HidingClassLoader} not by
|
||||
* the normal {@code ClassLoader}.
|
||||
*
|
||||
* @return list of classes not to shadow.
|
||||
*/
|
||||
Class[] shadowPackage() default {};
|
||||
|
||||
/**
|
||||
* prefixes of class names that will be loaded by the {@link HidingClassLoader}.
|
||||
*
|
||||
* @return list of class prefixes which will not be shadowed.
|
||||
*/
|
||||
String[] shadowByPrefix() default {};
|
||||
|
||||
/**
|
||||
* classes of which the package that contains them will be hidden by the {@link HidingClassLoader}.
|
||||
*
|
||||
* @return list of classes to hidePackage.
|
||||
*/
|
||||
Class[] hidePackage() default {};
|
||||
|
||||
/**
|
||||
* classes from packages that will be hidden by the {@link HidingClassLoader}.
|
||||
*
|
||||
* @return list of classes of which the package will be hidden.
|
||||
*/
|
||||
String[] hideByPrefix() default {};
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.classloadersupport;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.rules.MethodRule;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
/**
|
||||
* supports creation of tests that need to load classes with a {@link HidingClassLoader}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class ClassLoaderRule implements MethodRule {
|
||||
|
||||
public HidingClassLoader classLoader;
|
||||
|
||||
@Override
|
||||
public Statement apply(final Statement base, FrameworkMethod method, Object target) {
|
||||
|
||||
CombinedClassLoaderConfiguration combinedConfiguration = new CombinedClassLoaderConfiguration(
|
||||
method.getAnnotation(ClassLoaderConfiguration.class),
|
||||
method.getDeclaringClass().getAnnotation(ClassLoaderConfiguration.class)
|
||||
);
|
||||
|
||||
classLoader = createClassLoader(combinedConfiguration);
|
||||
|
||||
return new Statement() {
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
|
||||
try {
|
||||
base.evaluate();
|
||||
} finally {
|
||||
classLoader = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static HidingClassLoader createClassLoader(CombinedClassLoaderConfiguration configuration) {
|
||||
|
||||
HidingClassLoader classLoader = new HidingClassLoader(mergeHidden(configuration));
|
||||
|
||||
for (Class shadow : configuration.shadowPackages) {
|
||||
classLoader.excludeClass(shadow.getPackage().getName());
|
||||
}
|
||||
|
||||
for (String shadow : configuration.shadowByPrefix) {
|
||||
classLoader.excludePackage(shadow);
|
||||
}
|
||||
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
private static List<String> mergeHidden(CombinedClassLoaderConfiguration configuration) {
|
||||
|
||||
List<String> hidden = new ArrayList<String>();
|
||||
|
||||
for (Class aClass : configuration.hidePackages) {
|
||||
hidden.add(aClass.getPackage().getName());
|
||||
}
|
||||
|
||||
for (String aPackage : configuration.hideByPrefix) {
|
||||
hidden.add(aPackage);
|
||||
}
|
||||
|
||||
return hidden;
|
||||
}
|
||||
|
||||
private static class CombinedClassLoaderConfiguration {
|
||||
|
||||
final List<Class> shadowPackages = new ArrayList<Class>();
|
||||
final List<String> shadowByPrefix = new ArrayList<String>();
|
||||
final List<Class> hidePackages = new ArrayList<Class>();
|
||||
final List<String> hideByPrefix = new ArrayList<String>();
|
||||
|
||||
CombinedClassLoaderConfiguration(ClassLoaderConfiguration methodAnnotation, ClassLoaderConfiguration classAnnotation) {
|
||||
|
||||
mergeAnnotation(methodAnnotation);
|
||||
mergeAnnotation(classAnnotation);
|
||||
}
|
||||
|
||||
private void mergeAnnotation(ClassLoaderConfiguration methodAnnotation) {
|
||||
|
||||
if (methodAnnotation != null) {
|
||||
|
||||
shadowPackages.addAll(asList(methodAnnotation.shadowPackage()));
|
||||
shadowByPrefix.addAll(asList(methodAnnotation.shadowByPrefix()));
|
||||
hidePackages.addAll(asList(methodAnnotation.hidePackage()));
|
||||
hideByPrefix.addAll(asList(methodAnnotation.hideByPrefix()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.classloadersupport;
|
||||
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* is intended for testing code that depends on the presence/absence of certain classes.
|
||||
*
|
||||
* Classes can be:
|
||||
* <ul>
|
||||
* <li>shadowed: reloaded by this classloader no matter if they are loaded already by the SystemClassLoader</li>
|
||||
* <li>hidden: not loaded by this classloader no matter if they are loaded already by the SystemClassLoader. Trying to load these classes results in a {@link ClassNotFoundException}</li>
|
||||
* <li>all other classes get loaded by the SystemClassLoader</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class HidingClassLoader extends ShadowingClassLoader {
|
||||
|
||||
private final Collection<String> hidden;
|
||||
|
||||
HidingClassLoader(Collection<String> hidden) {
|
||||
super(URLClassLoader.getSystemClassLoader());
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
|
||||
checkIfHidden(name);
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
|
||||
checkIfHidden(name);
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
private void checkIfHidden(String name) throws ClassNotFoundException {
|
||||
|
||||
for (String prefix : hidden) {
|
||||
if (name.startsWith(prefix)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.classloadersupport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.DecoratingClassLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* ClassLoader decorator that shadows an enclosing ClassLoader,
|
||||
* applying registered transformers to all affected classes.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Costin Leau
|
||||
* @since 2.0
|
||||
* @see #addTransformer
|
||||
* @see org.springframework.core.OverridingClassLoader
|
||||
*/
|
||||
public class ShadowingClassLoader extends DecoratingClassLoader {
|
||||
|
||||
/** Packages that are excluded by default */
|
||||
public static final String[] DEFAULT_EXCLUDED_PACKAGES =
|
||||
new String[] {"java.", "javax.", "sun.", "oracle.", "com.sun.", "com.ibm.", "COM.ibm.",
|
||||
"org.w3c.", "org.xml.", "org.dom4j.", "org.eclipse", "org.aspectj.", "net.sf.cglib",
|
||||
"org.springframework.cglib", "org.apache.xerces.", "org.apache.commons.logging."};
|
||||
|
||||
|
||||
private final ClassLoader enclosingClassLoader;
|
||||
|
||||
private final List<ClassFileTransformer> classFileTransformers = new LinkedList<ClassFileTransformer>();
|
||||
|
||||
private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ShadowingClassLoader, decorating the given ClassLoader.
|
||||
* @param enclosingClassLoader the ClassLoader to decorate
|
||||
*/
|
||||
public ShadowingClassLoader(ClassLoader enclosingClassLoader) {
|
||||
Assert.notNull(enclosingClassLoader, "Enclosing ClassLoader must not be null");
|
||||
this.enclosingClassLoader = enclosingClassLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the given ClassFileTransformer to the list of transformers that this
|
||||
* ClassLoader will apply.
|
||||
* @param transformer the ClassFileTransformer
|
||||
*/
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
Assert.notNull(transformer, "Transformer must not be null");
|
||||
this.classFileTransformers.add(transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all ClassFileTransformers from the given ClassLoader to the list of
|
||||
* transformers that this ClassLoader will apply.
|
||||
* @param other the ClassLoader to copy from
|
||||
*/
|
||||
public void copyTransformers(ShadowingClassLoader other) {
|
||||
Assert.notNull(other, "Other ClassLoader must not be null");
|
||||
this.classFileTransformers.addAll(other.classFileTransformers);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
if (shouldShadow(name)) {
|
||||
System.out.println("shadowing " + name);
|
||||
Class<?> cls = this.classCache.get(name);
|
||||
if (cls != null) {
|
||||
return cls;
|
||||
}
|
||||
return doLoadClass(name);
|
||||
}
|
||||
else {
|
||||
return this.enclosingClassLoader.loadClass(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given class should be excluded from shadowing.
|
||||
* @param className the name of the class
|
||||
* @return whether the specified class should be shadowed
|
||||
*/
|
||||
private boolean shouldShadow(String className) {
|
||||
return (!className.equals(getClass().getName()) && !className.endsWith("ShadowingClassLoader") &&
|
||||
isEligibleForShadowing(className));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the specified class is eligible for shadowing
|
||||
* by this class loader.
|
||||
* @param className the class name to check
|
||||
* @return whether the specified class is eligible
|
||||
* @see #isExcluded
|
||||
*/
|
||||
protected boolean isEligibleForShadowing(String className) {
|
||||
return isExcluded(className);
|
||||
}
|
||||
|
||||
|
||||
private Class<?> doLoadClass(String name) throws ClassNotFoundException {
|
||||
String internalName = StringUtils.replace(name, ".", "/") + ".class";
|
||||
InputStream is = this.enclosingClassLoader.getResourceAsStream(internalName);
|
||||
if (is == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
try {
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(is);
|
||||
bytes = applyTransformers(name, bytes);
|
||||
Class<?> cls = defineClass(name, bytes, 0, bytes.length);
|
||||
// Additional check for defining the package, if not defined yet.
|
||||
if (cls.getPackage() == null) {
|
||||
int packageSeparator = name.lastIndexOf('.');
|
||||
if (packageSeparator != -1) {
|
||||
String packageName = name.substring(0, packageSeparator);
|
||||
definePackage(packageName, null, null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
this.classCache.put(name, cls);
|
||||
return cls;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] applyTransformers(String name, byte[] bytes) {
|
||||
String internalName = StringUtils.replace(name, ".", "/");
|
||||
try {
|
||||
for (ClassFileTransformer transformer : this.classFileTransformers) {
|
||||
byte[] transformed = transformer.transform(this, internalName, null, null, bytes);
|
||||
bytes = (transformed != null ? transformed : bytes);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
catch (IllegalClassFormatException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
return this.enclosingClassLoader.getResource(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
return this.enclosingClassLoader.getResourceAsStream(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
return this.enclosingClassLoader.getResources(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
|
||||
|
||||
/**
|
||||
* Helper methods for web integration testing.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class WebTestUtils {
|
||||
@@ -41,13 +41,28 @@ public class WebTestUtils {
|
||||
|
||||
/**
|
||||
* Creates a {@link WebApplicationContext} from the given configuration classes.
|
||||
*
|
||||
*
|
||||
* @param configClasses
|
||||
* @return
|
||||
*/
|
||||
public static WebApplicationContext createApplicationContext(Class<?>... configClasses) {
|
||||
return createApplicationContext(null, configClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebApplicationContext} from the given configuration classes.
|
||||
*
|
||||
* @param classLoader gets set as ClassLoader in the context
|
||||
* @param configClasses
|
||||
* @return
|
||||
*/
|
||||
public static WebApplicationContext createApplicationContext(ClassLoader classLoader, Class<?>... configClasses) {
|
||||
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
if (classLoader != null) {
|
||||
context.setClassLoader(classLoader);
|
||||
}
|
||||
|
||||
context.setServletContext(new MockServletContext());
|
||||
|
||||
for (Class<?> configClass : configClasses) {
|
||||
|
||||
@@ -20,56 +20,62 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.classloadersupport.ClassLoaderConfiguration;
|
||||
import org.springframework.data.classloadersupport.ClassLoaderRule;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.web.PagedResourcesAssemblerArgumentResolver;
|
||||
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.web.WebTestUtils;
|
||||
import org.springframework.data.web.config.EnableSpringDataWebSupport.SpringDataWebConfigurationImportSelector;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link EnableSpringDataWebSupport}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
|
||||
@ClassLoaderConfiguration(shadowPackage = {
|
||||
WebMvcConfigurationSupport.class,
|
||||
EnableSpringDataWebSupport.class,
|
||||
EnableSpringDataWebSupportIntegrationTests.SampleConfig.class})
|
||||
public class EnableSpringDataWebSupportIntegrationTests {
|
||||
|
||||
private static final String HATEOAS = "HATEOAS_PRESENT";
|
||||
private static final String JACKSON = "JACKSON_PRESENT";
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@EnableSpringDataWebSupport
|
||||
static class SampleConfig {
|
||||
|
||||
public @Bean SampleController controller() {
|
||||
public
|
||||
@Bean
|
||||
SampleController controller() {
|
||||
return new SampleController();
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
reEnable(HATEOAS);
|
||||
reEnable(JACKSON);
|
||||
}
|
||||
@Rule public ClassLoaderRule classLoaderRule = new ClassLoaderRule();
|
||||
|
||||
|
||||
@Test // DATACMNS-330
|
||||
public void registersBasicBeanDefinitions() throws Exception {
|
||||
@@ -94,11 +100,11 @@ public class EnableSpringDataWebSupportIntegrationTests {
|
||||
}
|
||||
|
||||
@Test // DATACMNS-330
|
||||
@ClassLoaderConfiguration(hidePackage = Link.class)
|
||||
public void doesNotRegisterHateoasSpecificComponentsIfHateoasNotPresent() throws Exception {
|
||||
|
||||
hide(HATEOAS);
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(classLoaderRule.classLoader, SampleConfig.class);
|
||||
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(SampleConfig.class);
|
||||
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
|
||||
|
||||
assertThat(names).contains("pageableResolver", "sortResolver");
|
||||
@@ -115,11 +121,11 @@ public class EnableSpringDataWebSupportIntegrationTests {
|
||||
}
|
||||
|
||||
@Test // DATACMNS-475
|
||||
@ClassLoaderConfiguration(hidePackage = com.fasterxml.jackson.databind.ObjectMapper.class)
|
||||
public void doesNotRegisterJacksonSpecificComponentsIfJacksonNotPresent() throws Exception {
|
||||
|
||||
hide(JACKSON);
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(classLoaderRule.classLoader, SampleConfig.class);
|
||||
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(SampleConfig.class);
|
||||
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
|
||||
|
||||
assertThat(names).doesNotContain("jacksonGeoModule");
|
||||
@@ -173,18 +179,4 @@ public class EnableSpringDataWebSupportIntegrationTests {
|
||||
|
||||
Arrays.asList(resolverTypes).forEach(type -> assertThat(resolvers).hasAtLeastOneElementOfType(type));
|
||||
}
|
||||
|
||||
private static void hide(String module) throws Exception {
|
||||
|
||||
Field field = ReflectionUtils.findField(SpringDataWebConfigurationImportSelector.class, module);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, false);
|
||||
}
|
||||
|
||||
private static void reEnable(String module) {
|
||||
|
||||
Field field = ReflectionUtils.findField(SpringDataWebConfigurationImportSelector.class, module);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,17 @@ package org.springframework.data.web.config;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.classloadersupport.ClassLoaderConfiguration;
|
||||
import org.springframework.data.classloadersupport.ClassLoaderRule;
|
||||
import org.springframework.data.web.ProjectingJackson2HttpMessageConverter;
|
||||
import org.springframework.data.web.XmlBeamHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.instrument.classloading.ShadowingClassLoader;
|
||||
|
||||
/**
|
||||
* Integration test for {@link SpringDataWebConfiguration}.
|
||||
@@ -42,32 +39,37 @@ import org.springframework.instrument.classloading.ShadowingClassLoader;
|
||||
*/
|
||||
public class SpringDataWebConfigurationIntegrationTests {
|
||||
|
||||
@Rule public ClassLoaderRule classLoader = new ClassLoaderRule();
|
||||
|
||||
@Test // DATACMNS-987
|
||||
@ClassLoaderConfiguration(hidePackage = com.fasterxml.jackson.databind.ObjectMapper.class)
|
||||
public void shouldNotLoadJacksonConverterWhenJacksonNotPresent() {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
createConfigWithClassLoaderExcluding("com.fasterxml.jackson").extendMessageConverters(converters);
|
||||
createConfigWithClassLoader().extendMessageConverters(converters);
|
||||
|
||||
assertThat(converters, not(hasItem(instanceWithClassName(ProjectingJackson2HttpMessageConverter.class))));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-987
|
||||
@ClassLoaderConfiguration(hidePackage = com.jayway.jsonpath.DocumentContext.class)
|
||||
public void shouldNotLoadJacksonConverterWhenJaywayNotPresent() {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
createConfigWithClassLoaderExcluding("com.jayway").extendMessageConverters(converters);
|
||||
createConfigWithClassLoader().extendMessageConverters(converters);
|
||||
|
||||
assertThat(converters, not(hasItem(instanceWithClassName(ProjectingJackson2HttpMessageConverter.class))));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-987
|
||||
@ClassLoaderConfiguration(hidePackage = org.xmlbeam.ProjectionFactory.class)
|
||||
public void shouldNotLoadXBeamConverterWhenXBeamNotPresent() throws Exception {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
createConfigWithClassLoaderExcluding("org.xmlbeam").extendMessageConverters(converters);
|
||||
createConfigWithClassLoader().extendMessageConverters(converters);
|
||||
|
||||
assertThat(converters, not(hasItem(instanceWithClassName(XmlBeamHttpMessageConverter.class))));
|
||||
}
|
||||
@@ -77,17 +79,17 @@ public class SpringDataWebConfigurationIntegrationTests {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
createConfigWithClassLoaderExcluding("load.everything").extendMessageConverters(converters);
|
||||
createConfigWithClassLoader().extendMessageConverters(converters);
|
||||
|
||||
assertThat(converters, hasItem(instanceWithClassName(XmlBeamHttpMessageConverter.class)));
|
||||
assertThat(converters, hasItem(instanceWithClassName(ProjectingJackson2HttpMessageConverter.class)));
|
||||
}
|
||||
|
||||
private static SpringDataWebConfiguration createConfigWithClassLoaderExcluding(String excludedClassNamePrefix) {
|
||||
private SpringDataWebConfiguration createConfigWithClassLoader() {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
SpringDataWebConfiguration.class);
|
||||
context.setClassLoader(initClassLoader(excludedClassNamePrefix));
|
||||
context.setClassLoader(classLoader.classLoader);
|
||||
|
||||
try {
|
||||
return context.getBean(SpringDataWebConfiguration.class);
|
||||
@@ -106,38 +108,4 @@ public class SpringDataWebConfigurationIntegrationTests {
|
||||
private static <T> Matcher<T> instanceWithClassName(Class<T> expectedClass) {
|
||||
return hasProperty("class", hasProperty("name", equalTo(expectedClass.getName())));
|
||||
}
|
||||
|
||||
private static ClassLoader initClassLoader(final String excludedClassNamePrefix) {
|
||||
|
||||
return new ShadowingClassLoader(URLClassLoader.getSystemClassLoader()) {
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
|
||||
if (name.startsWith(excludedClassNamePrefix)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
|
||||
if (name.startsWith(excludedClassNamePrefix)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
|
||||
return super.findClass(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class ObjectFactoryImpl implements ObjectFactory<ConversionService> {
|
||||
|
||||
@Override
|
||||
public ConversionService getObject() throws BeansException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user