Skip to content

Commit

Permalink
issue #624 - adding possibility of using provided ClassLoader on Type…
Browse files Browse the repository at this point in the history
…Factory
  • Loading branch information
lufegit committed Jun 30, 2015
1 parent 8e9da38 commit cfe88fe
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 12 deletions.
14 changes: 13 additions & 1 deletion pom.xml
Expand Up @@ -80,7 +80,19 @@ javax.xml.datatype, javax.xml.namespace, javax.xml.parsers
<!-- and for testing we need a few libraries
libs for which we use reflection for code, but direct dep for testing
-->

<!-- Mock -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<!-- For testing TestNoClassDefFoundDeserializer -->
<dependency>
<groupId>javax.measure</groupId>
Expand Down
59 changes: 48 additions & 11 deletions src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
Expand Up @@ -98,6 +98,13 @@ public final class TypeFactory
protected final TypeModifier[] _modifiers;

protected final TypeParser _parser;

/**
* ClassLoader used by this factory (Issue #624)
*/
protected final ClassLoader _classLoader;

protected boolean isDefaultInstance;

/*
/**********************************************************
Expand All @@ -108,22 +115,34 @@ public final class TypeFactory
private TypeFactory() {
_parser = new TypeParser(this);
_modifiers = null;
_classLoader = null;
isDefaultInstance = true;
}

protected TypeFactory(TypeParser p, TypeModifier[] mods) {
this(p, mods, null);
}

protected TypeFactory(TypeParser p, TypeModifier[] mods, ClassLoader classLoader) {
_parser = p;
_modifiers = mods;
_classLoader = classLoader;
isDefaultInstance = false;
}

public TypeFactory withModifier(TypeModifier mod)
{
if (mod == null) { // mostly for unit tests
return new TypeFactory(_parser, _modifiers);
}
if (_modifiers == null) {
return new TypeFactory(_parser, new TypeModifier[] { mod });
}
return new TypeFactory(_parser, ArrayBuilders.insertInListNoDup(_modifiers, mod));
if (mod == null) { // mostly for unit tests
return new TypeFactory(_parser, _modifiers, _classLoader);
}
if (_modifiers == null) {
return new TypeFactory(_parser, new TypeModifier[] { mod }, _classLoader);
}
return new TypeFactory(_parser, ArrayBuilders.insertInListNoDup(_modifiers, mod), _classLoader);
}

public TypeFactory withClassLoader(ClassLoader classLoader) {
return new TypeFactory(_parser, _modifiers, classLoader);
}

/**
Expand All @@ -147,6 +166,13 @@ public void clearCache() {
_typeCache.clear();
}

/*
* Getters
*/
public ClassLoader getClassLoader() {
return _classLoader;
}

/*
/**********************************************************
/* Static methods for non-instance-specific functionality
Expand Down Expand Up @@ -198,17 +224,19 @@ public Class<?> findClass(String className) throws ClassNotFoundException
}
// Two-phase lookup: first using context ClassLoader; then default
Throwable prob = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();

ClassLoader loader = this.getClassLoader();
if (loader == null) {
loader = Thread.currentThread().getContextClassLoader();
}
if (loader != null) {
try {
return Class.forName(className, true, loader);
return classForName(className, true, loader);
} catch (Exception e) {
prob = ClassUtil.getRootCause(e);
}
}
try {
return Class.forName(className);
return classForName(className);
} catch (Exception e) {
if (prob == null) {
prob = ClassUtil.getRootCause(e);
Expand All @@ -219,6 +247,15 @@ public Class<?> findClass(String className) throws ClassNotFoundException
}
throw new ClassNotFoundException(prob.getMessage(), prob);
}

protected Class<?> classForName(String name, boolean initialize,
ClassLoader loader) throws ClassNotFoundException {
return Class.forName(name, true, loader);
}

protected Class<?> classForName(String name) throws ClassNotFoundException {
return Class.forName(name);
}

protected Class<?> _findPrimitive(String className)
{
Expand Down
@@ -0,0 +1,147 @@
package com.fasterxml.jackson.databind.type;

import static org.mockito.Mockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;

@RunWith(PowerMockRunner.class)
@PrepareForTest(TypeFactory.class)

public class TestTypeFactoryWithClassLoader {
@Mock
private TypeModifier typeModifier;
private static ClassLoader classLoader;
private static ClassLoader threadClassLoader;
private static String aClassName;
private ObjectMapper objectMapper;

@BeforeClass
public static void beforeClass() {
classLoader = AClass.class.getClassLoader();
aClassName = AClass.getStaticClassName();
threadClassLoader = Thread.currentThread().getContextClassLoader();
Assert.assertNotNull(threadClassLoader);
}

@Before
public void before() {
objectMapper = new ObjectMapper();
}

@After
public void after() {
Thread.currentThread().setContextClassLoader(threadClassLoader);
objectMapper = null;
}

@Test
public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNull() throws ClassNotFoundException {
Thread.currentThread().setContextClassLoader(null);
TypeFactory spySut = spy(objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader));
Class<?> clazz = spySut.findClass(aClassName);
verify(spySut).getClassLoader();
verify(spySut).classForName(any(String.class), any(Boolean.class), eq(classLoader));
Assert.assertNotNull(clazz);
Assert.assertEquals(classLoader, spySut.getClassLoader());
Assert.assertEquals(typeModifier,spySut._modifiers[0]);
Assert.assertEquals(null, Thread.currentThread().getContextClassLoader());
}

@Test
public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNotNull() throws ClassNotFoundException {
TypeFactory spySut = spy(objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader));
Class<?> clazz = spySut.findClass(aClassName);
verify(spySut).getClassLoader();
verify(spySut).classForName(any(String.class), any(Boolean.class), eq(classLoader));
Assert.assertNotNull(clazz);
Assert.assertEquals(classLoader, spySut.getClassLoader());
Assert.assertEquals(typeModifier,spySut._modifiers[0]);
}

@Test
public void testCallingOnlyWithModifierGivesExpectedResults(){
TypeFactory sut = objectMapper.getTypeFactory().withModifier(typeModifier);
Assert.assertNull(sut.getClassLoader());
Assert.assertEquals(typeModifier,sut._modifiers[0]);
}

@Test
public void testCallingOnlyWithClassLoaderGivesExpectedResults(){
TypeFactory sut = objectMapper.getTypeFactory().withClassLoader(classLoader);
Assert.assertNotNull(sut.getClassLoader());
Assert.assertArrayEquals(null,sut._modifiers);
}

@Test
public void testDefaultTypeFactoryNotAffectedByWithConstructors() {
TypeFactory sut = objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader);
Assert.assertEquals(classLoader, sut.getClassLoader());
Assert.assertEquals(typeModifier,sut._modifiers[0]);
Assert.assertNull(objectMapper.getTypeFactory().getClassLoader());
Assert.assertArrayEquals(null,objectMapper.getTypeFactory()._modifiers);
}

@Test
public void testSetsTheCorrectClassLoderIfUsingWithModifierFollowedByWithClassLoader() {
TypeFactory sut = objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader);
Assert.assertNotNull(sut.getClassLoader());
}

@Test
public void testSetsTheCorrectClassLoderIfUsingWithClassLoaderFollowedByWithModifier() {
TypeFactory sut = objectMapper.getTypeFactory().withClassLoader(classLoader).withModifier(typeModifier);
Assert.assertNotNull(sut.getClassLoader());
}

@Test
public void testThreadContextClassLoaderIsUsedIfNotUsingWithClassLoader() throws ClassNotFoundException {
TypeFactory spySut = spy(objectMapper.getTypeFactory());
Assert.assertNull(spySut.getClassLoader());
Class<?> clazz = spySut.findClass(aClassName);
Assert.assertNotNull(clazz);
verify(spySut).classForName(any(String.class), any(Boolean.class), eq(threadClassLoader));
}

@Test
public void testUsesFallBackClassLoaderIfNoThreadClassLoaderAndNoWithClassLoader() throws ClassNotFoundException {
Thread.currentThread().setContextClassLoader(null);
TypeFactory spySut = spy(objectMapper.getTypeFactory());
Assert.assertNull(spySut.getClassLoader());
Assert.assertArrayEquals(null,spySut._modifiers);
Class<?> clazz = spySut.findClass(aClassName);
Assert.assertNotNull(clazz);
verify(spySut).classForName(any(String.class));
}

public static class AClass
{
private String _foo, _bar;
protected final static Class<?> thisClass = new Object() {
}.getClass().getEnclosingClass();

public AClass() { }
public AClass(String foo, String bar) {
_foo = foo;
_bar = bar;
}
public String getFoo() { return _foo; }
public String getBar() { return _bar; }

public void setFoo(String foo) { _foo = foo; }
public void setBar(String bar) { _bar = bar; }
public static String getStaticClassName() {
return thisClass.getCanonicalName().replace("."+thisClass.getSimpleName(), "$"+thisClass.getSimpleName());
}
}
}

0 comments on commit cfe88fe

Please sign in to comment.