diff --git a/src/main/java/org/apache/commons/beanutils2/BeanPredicate.java b/src/main/java/org/apache/commons/beanutils2/BeanPredicate.java index 6bf1a62ad..ad747cac7 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanPredicate.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanPredicate.java @@ -21,7 +21,6 @@ import java.util.function.Predicate; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

Predicate implementation that applies the given {@code Predicate} @@ -32,7 +31,7 @@ */ public class BeanPredicate implements Predicate { - private final Log log = LogFactory.getLog(this.getClass()); + private final Log log = LoggerUtil.createLoggerWithContextClassLoader(this.getClass()); /** Name of the property whose value will be predicated */ private String propertyName; diff --git a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java index 935d7a576..d4dc1c055 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java @@ -21,7 +21,6 @@ import java.util.function.Consumer; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

{@code Closure} that sets a property.

@@ -81,7 +80,7 @@ public class BeanPropertyValueChangeConsumer implements Consumer { /** For logging. Each subclass gets its own log instance. */ - private final Log log = LogFactory.getLog(this.getClass()); + private final Log log = LoggerUtil.createLoggerWithContextClassLoader(this.getClass()); /** * The name of the property which will be updated when this {@code Closure} executes. diff --git a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java index c090569f3..44fc89b5f 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java @@ -22,7 +22,6 @@ import java.util.function.Predicate; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

{@code Predicate} that evaluates a property value against a specified value.

@@ -113,7 +112,7 @@ public class BeanPropertyValueEqualsPredicate implements Predicate { /** For logging. Each subclass gets its own log instance. */ - private final Log log = LogFactory.getLog(this.getClass()); + private final Log log = LoggerUtil.createLoggerWithContextClassLoader(this.getClass()); /** * The name of the property which will be evaluated when this {@code Predicate} is executed. diff --git a/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java b/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java index a50e99ddf..fb5205f64 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java @@ -21,7 +21,6 @@ import java.util.function.Function; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

{@code Transformer} that outputs a property value.

@@ -73,7 +72,7 @@ public class BeanToPropertyValueTransformer implements Function { /** For logging. Each subclass gets its own log instance. */ - private final Log log = LogFactory.getLog(this.getClass()); + private final Log log = LoggerUtil.createLoggerWithContextClassLoader(this.getClass()); /** The name of the property that will be used in the transformation of the object. */ private final String propertyName; diff --git a/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java index 9b81b2aa0..8cc9a54c0 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java @@ -30,7 +30,6 @@ import org.apache.commons.beanutils2.expression.Resolver; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

JavaBean property population methods.

@@ -60,7 +59,7 @@ protected BeanUtilsBean initialValue() { /** * Logging for this instance */ - private static final Log LOG = LogFactory.getLog(BeanUtilsBean.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(BeanUtilsBean.class); /** A reference to Throwable's initCause method, or null if it's not there in this JVM */ private static final Method INIT_CAUSE_METHOD = getInitCauseMethod(); @@ -95,13 +94,13 @@ private static Method getInitCauseMethod() { final Class[] paramsClasses = { Throwable.class }; return Throwable.class.getMethod("initCause", paramsClasses); } catch (final NoSuchMethodException e) { - final Log log = LogFactory.getLog(BeanUtils.class); + final Log log = LoggerUtil.createLoggerWithContextClassLoader(BeanUtils.class); if (log.isWarnEnabled()) { log.warn("Throwable does not have initCause() method in JDK 1.3"); } return null; } catch (final Throwable e) { - final Log log = LogFactory.getLog(BeanUtils.class); + final Log log = LoggerUtil.createLoggerWithContextClassLoader(BeanUtils.class); if (log.isWarnEnabled()) { log.warn("Error getting the Throwable initCause() method", e); } diff --git a/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java index 4ed832326..d6170faa1 100644 --- a/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java @@ -94,7 +94,6 @@ import org.apache.commons.beanutils2.sql.converters.SqlTimeConverter; import org.apache.commons.beanutils2.sql.converters.SqlTimestampConverter; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

Utility methods for converting String scalar values to objects of the @@ -196,7 +195,7 @@ public class ConvertUtilsBean { /** * The {@code Log} instance for this class. */ - private static final Log LOG = LogFactory.getLog(ConvertUtilsBean.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(ConvertUtilsBean.class); /** * Gets singleton instance diff --git a/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java index c05125032..5cb6dc530 100644 --- a/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java +++ b/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java @@ -25,7 +25,6 @@ import java.util.List; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

@@ -54,7 +53,7 @@ public class DefaultBeanIntrospector implements BeanIntrospector { private static final Class[] LIST_CLASS_PARAMETER = new Class[] { java.util.List.class }; /** For logging. Each subclass gets its own log instance. */ - private final Log log = LogFactory.getLog(getClass()); + private final Log log = LoggerUtil.createLoggerWithContextClassLoader(getClass()); /** * Private constructor so that no instances can be created. diff --git a/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java index c27a7f431..d2134fb60 100644 --- a/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java +++ b/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java @@ -24,7 +24,6 @@ import java.util.Objects; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

@@ -81,7 +80,7 @@ public class FluentPropertyBeanIntrospector implements BeanIntrospector { public static final String DEFAULT_WRITE_METHOD_PREFIX = "set"; /** The logger. */ - private final Log log = LogFactory.getLog(getClass()); + private final Log log = LoggerUtil.createLoggerWithContextClassLoader(getClass()); /** The prefix of write methods to search for. */ private final String writeMethodPrefix; diff --git a/src/main/java/org/apache/commons/beanutils2/LazyDynaBean.java b/src/main/java/org/apache/commons/beanutils2/LazyDynaBean.java index 136a432b3..742bbea19 100644 --- a/src/main/java/org/apache/commons/beanutils2/LazyDynaBean.java +++ b/src/main/java/org/apache/commons/beanutils2/LazyDynaBean.java @@ -28,8 +28,6 @@ import java.util.Objects; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - /** *

DynaBean which automatically adds properties to the {@code DynaClass} * and provides Lazy List and Lazy Map features.

@@ -128,7 +126,7 @@ public class LazyDynaBean implements DynaBean, Serializable { /** * Commons Logging */ - private static transient Log LOG = LogFactory.getLog(LazyDynaBean.class); + private static transient Log LOG = LoggerUtil.createLoggerWithContextClassLoader(LazyDynaBean.class); /** BigInteger Zero */ protected static final BigInteger BigInteger_ZERO = new BigInteger("0"); @@ -692,7 +690,7 @@ protected boolean isDynaProperty(final String name) { */ private Log logger() { if (LOG == null) { - LOG = LogFactory.getLog(LazyDynaBean.class); + LOG = LoggerUtil.createLoggerWithContextClassLoader(LazyDynaBean.class); } return LOG; } diff --git a/src/main/java/org/apache/commons/beanutils2/LoggerUtil.java b/src/main/java/org/apache/commons/beanutils2/LoggerUtil.java new file mode 100644 index 000000000..783737f37 --- /dev/null +++ b/src/main/java/org/apache/commons/beanutils2/LoggerUtil.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.beanutils2; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class LoggerUtil { + + @SuppressWarnings("rawtypes") + public static Log createLoggerWithContextClassLoader(Class clazz) { + ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); + Log log = null; + try { + Thread.currentThread().setContextClassLoader(clazz.getClassLoader()); + log = LogFactory.getLog(clazz); + } finally { + Thread.currentThread().setContextClassLoader(currentContextClassLoader); + } + + if (log == null) { + return LogFactory.getLog(clazz); // fallback -- is this needed? + } + + return log; + } +} diff --git a/src/main/java/org/apache/commons/beanutils2/MethodUtils.java b/src/main/java/org/apache/commons/beanutils2/MethodUtils.java index 61a47ca52..9697a0b3c 100644 --- a/src/main/java/org/apache/commons/beanutils2/MethodUtils.java +++ b/src/main/java/org/apache/commons/beanutils2/MethodUtils.java @@ -30,7 +30,6 @@ import org.apache.commons.lang3.SystemProperties; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

Utility reflection methods focused on methods in general rather than properties in particular.

@@ -110,7 +109,7 @@ public int hashCode() { } } - private static final Log LOG = LogFactory.getLog(MethodUtils.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(MethodUtils.class); /** * Only log warning about accessibility work around once. diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java index 893adb863..07d2d4a08 100644 --- a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java @@ -34,7 +34,6 @@ import org.apache.commons.beanutils2.expression.DefaultResolver; import org.apache.commons.beanutils2.expression.Resolver; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Utility methods for using Java Reflection APIs to facilitate generic @@ -91,7 +90,7 @@ public class PropertyUtilsBean { /** Log instance */ - private static final Log LOG = LogFactory.getLog(PropertyUtilsBean.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(PropertyUtilsBean.class); /** * Gets the PropertyUtils bean instance. diff --git a/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java index 00f070caa..aa0b6fa8d 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java @@ -25,8 +25,8 @@ import org.apache.commons.beanutils2.ConversionException; import org.apache.commons.beanutils2.ConvertUtils; import org.apache.commons.beanutils2.Converter; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Base {@link Converter} implementation that provides the structure @@ -420,7 +420,7 @@ public boolean isUseDefault() { */ Log log() { if (log == null) { - log = LogFactory.getLog(getClass()); + log = LoggerUtil.createLoggerWithContextClassLoader(getClass()); } return log; } diff --git a/src/main/java/org/apache/commons/beanutils2/locale/BaseLocaleConverter.java b/src/main/java/org/apache/commons/beanutils2/locale/BaseLocaleConverter.java index 0186d80f0..96cace9ce 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/BaseLocaleConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/BaseLocaleConverter.java @@ -22,8 +22,8 @@ import org.apache.commons.beanutils2.ConversionException; import org.apache.commons.beanutils2.ConvertUtils; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * The base class for all standard type locale-sensitive converters. It has {@link LocaleConverter} and {@link org.apache.commons.beanutils2.Converter} @@ -132,7 +132,7 @@ public B setUseDefault(final boolean useDefault) { } /** All logging goes through this logger */ - private static final Log LOG = LogFactory.getLog(BaseLocaleConverter.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(BaseLocaleConverter.class); /** * Checks whether the result of a conversion is conform to the specified target type. If this is the case, the passed in result object is cast to the diff --git a/src/main/java/org/apache/commons/beanutils2/locale/LocaleBeanUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/locale/LocaleBeanUtilsBean.java index 5f9386ad2..6746af239 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/LocaleBeanUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/LocaleBeanUtilsBean.java @@ -29,11 +29,11 @@ import org.apache.commons.beanutils2.DynaBean; import org.apache.commons.beanutils2.DynaClass; import org.apache.commons.beanutils2.DynaProperty; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.beanutils2.MappedPropertyDescriptor; import org.apache.commons.beanutils2.PropertyUtilsBean; import org.apache.commons.beanutils2.expression.Resolver; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

Utility methods for populating JavaBeans properties @@ -56,7 +56,7 @@ protected LocaleBeanUtilsBean initialValue() { }; /** All logging goes through this logger */ - private static final Log LOG = LogFactory.getLog(LocaleBeanUtilsBean.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(LocaleBeanUtilsBean.class); /** * Gets singleton instance diff --git a/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java index c08b1afaf..057d72397 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java @@ -24,6 +24,7 @@ import java.util.Map; import org.apache.commons.beanutils2.ConversionException; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.beanutils2.WeakFastHashMap; import org.apache.commons.beanutils2.locale.converters.BigDecimalLocaleConverter; import org.apache.commons.beanutils2.locale.converters.BigIntegerLocaleConverter; @@ -38,7 +39,6 @@ import org.apache.commons.beanutils2.sql.converters.locale.SqlTimeLocaleConverter; import org.apache.commons.beanutils2.sql.converters.locale.SqlTimestampLocaleConverter; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** *

Utility methods for converting locale-sensitive String scalar values to objects of the @@ -82,7 +82,7 @@ public class LocaleConvertUtilsBean { /** The {@code Log} instance for this class. */ - private static final Log LOG = LogFactory.getLog(LocaleConvertUtilsBean.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(LocaleConvertUtilsBean.class); /** * Gets singleton instance. diff --git a/src/main/java/org/apache/commons/beanutils2/locale/converters/DateLocaleConverter.java b/src/main/java/org/apache/commons/beanutils2/locale/converters/DateLocaleConverter.java index 30714bf22..0bfc3fb81 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/converters/DateLocaleConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/converters/DateLocaleConverter.java @@ -26,10 +26,10 @@ import java.util.Locale; import org.apache.commons.beanutils2.ConversionException; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.beanutils2.locale.BaseLocaleConverter; import org.apache.commons.beanutils2.locale.LocaleConverter; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Standard {@link org.apache.commons.beanutils2.locale.LocaleConverter} implementation that converts an incoming locale-sensitive String into a @@ -94,7 +94,7 @@ public B setLenient(final boolean lenient) { private static final String DEFAULT_PATTERN_CHARS = DateLocaleConverter.initDefaultChars(); /** All logging goes through this logger */ - private static final Log LOG = LogFactory.getLog(DateLocaleConverter.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(DateLocaleConverter.class); /** * Constructs a new builder. diff --git a/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java b/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java index 679b85b2e..66e8ede22 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java @@ -23,10 +23,10 @@ import java.util.Locale; import org.apache.commons.beanutils2.ConversionException; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.beanutils2.locale.BaseLocaleConverter; import org.apache.commons.beanutils2.locale.LocaleConverter; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Standard {@link LocaleConverter} implementation that converts an incoming locale-sensitive String into a {@link Number} object, optionally using a @@ -62,7 +62,7 @@ public DecimalLocaleConverter get() { } /** All logging goes through this logger */ - private static final Log LOG = LogFactory.getLog(DecimalLocaleConverter.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(DecimalLocaleConverter.class); /** * Constructs a new builder. diff --git a/src/main/java/org/apache/commons/beanutils2/locale/converters/StringLocaleConverter.java b/src/main/java/org/apache/commons/beanutils2/locale/converters/StringLocaleConverter.java index a129eb9fb..64520a625 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/converters/StringLocaleConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/converters/StringLocaleConverter.java @@ -27,10 +27,10 @@ import java.util.Locale; import org.apache.commons.beanutils2.ConversionException; +import org.apache.commons.beanutils2.LoggerUtil; import org.apache.commons.beanutils2.locale.BaseLocaleConverter; import org.apache.commons.beanutils2.locale.LocaleConverter; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Standard {@link org.apache.commons.beanutils2.locale.LocaleConverter} implementation that converts an incoming locale-sensitive object into a {@link String} @@ -60,7 +60,7 @@ public StringLocaleConverter get() { } /** All logging goes through this logger */ - private static final Log LOG = LogFactory.getLog(StringLocaleConverter.class); + private static final Log LOG = LoggerUtil.createLoggerWithContextClassLoader(StringLocaleConverter.class); /** * Constructs a new builder. diff --git a/src/test/java/org/apache/commons/beanutils2/bugs/Jira566TestCase.java b/src/test/java/org/apache/commons/beanutils2/bugs/Jira566TestCase.java new file mode 100644 index 000000000..b38a5f0e2 --- /dev/null +++ b/src/test/java/org/apache/commons/beanutils2/bugs/Jira566TestCase.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.beanutils2.bugs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.commons.beanutils2.LoggerUtil; +import org.apache.commons.logging.LogFactory; +import org.junit.jupiter.api.Test; + +/** + * Test class for {@code LoggerUtil} + * https://issues.apache.org/jira/browse/BEANUTILS-566 + */ +public class Jira566TestCase { + + private static CustomURLClassLoader CUSTOM_CLASSLOADER; + + private static Boolean IS_CUSTOM_CLASSLOADER_INVOKED = false; + + private static class CustomURLClassLoader extends URLClassLoader { + + public CustomURLClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + /* + * Given the following default loadClass implementation steps (in order, per the JavaDoc): + * 1) Invoke findLoadedClass(String) to check if the class has already been loaded. + * 2) Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead. + * 3) Invoke the findClass(String) method to find the class. + * + * If the name matches Jira566TestCase, then we find that class with this classloader instead via findClass. + * Calling newInstance().getClass().getClassLoader() on the returned class object would an instance of CustomURLClassLoader. + * Otherwise, if findLoadedClass or loadClass are used instead, then the original classloader is used (i.e jdk.internal.loader.ClassLoaders$AppClassLoader) + * + */ + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (name.equals("org.apache.commons.beanutils2.bugs.Jira566TestCase")) { + // System.out.println("match: " + name); // debug + return findClass(name); + } else { + // System.out.println("super: " + name); // debug + IS_CUSTOM_CLASSLOADER_INVOKED = true; + return super.loadClass(name); + } + } + + } + + + public static void setup() throws Exception { + Path targetPath = Paths.get(Jira566TestCase.class.getResource("/").toURI()).getParent(); + ClassLoader parent = Jira566TestCase.class.getClassLoader(); + CUSTOM_CLASSLOADER = new CustomURLClassLoader(new URL[] {new File(targetPath.toString(), "test-classes").toURI().toURL()}, parent); + } + + /* + * Tests that context classloader is used + */ + @Test + public void testContextClassLoaderUsed() throws Exception { + + setup(); + + /* + * Create an instance of this class (Jira566TestCase) so that it's classloader is CustomURLClassLoader + */ + Class loadedClass = CUSTOM_CLASSLOADER.loadClass("org.apache.commons.beanutils2.bugs.Jira566TestCase"); + Class instance = loadedClass.newInstance().getClass(); + + IS_CUSTOM_CLASSLOADER_INVOKED = false; // reset to false + + /* + * When the logger is created, the creation will go through CustomURLClassLoader#loadClass + */ + LoggerUtil.createLoggerWithContextClassLoader(instance); + + assertTrue("Context ClassLoader was not invoked", IS_CUSTOM_CLASSLOADER_INVOKED); + } + + /* + * Tests that original classloader is used + */ + @Test + public void testOriginalClassLoaderUsed() throws Exception { + + setup(); + + IS_CUSTOM_CLASSLOADER_INVOKED = false; // reset to false + LogFactory.getLog(Jira566TestCase.class); + + assertFalse("Context ClassLoader was invoked when it should not have been", IS_CUSTOM_CLASSLOADER_INVOKED); + } + + /* + * The LoggerUtil.createLoggerWithContextClassLoader method swaps out the classloaders, + * so this test checks to make it the original is set back. + */ + @Test + public void testClassLoaderIsSetBackToOriginalInLoggerUtil() throws Exception { + + ClassLoader original = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(CUSTOM_CLASSLOADER); + LoggerUtil.createLoggerWithContextClassLoader(Jira566TestCase.class); + + assertEquals("Classloader do not match!",Thread.currentThread().getContextClassLoader(), CUSTOM_CLASSLOADER); + } finally { + // set it back to avoid any problems in the other tests + Thread.currentThread().setContextClassLoader(original); + } + + } +}