Repository: camel Updated Branches: refs/heads/fix/CAMEL-8689-camel-dozer-classloading-issue [created] 87eebc6c0
CAMEL-8689 Fix Camel Dozer class loading issue when using multiple contexts with different classloaders. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/87eebc6c Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/87eebc6c Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/87eebc6c Branch: refs/heads/fix/CAMEL-8689-camel-dozer-classloading-issue Commit: 87eebc6c04f5364c6ebdab15e9c69efa40e5c5cd Parents: 42de535 Author: Raul Kripalani <ra...@apache.org> Authored: Thu Apr 23 13:46:35 2015 +0100 Committer: Raul Kripalani <ra...@apache.org> Committed: Thu Apr 23 13:46:35 2015 +0100 ---------------------------------------------------------------------- .../dozer/CamelToDozerClassResolverAdapter.java | 46 ----------- .../converter/dozer/DozerTypeConverter.java | 23 +++++- .../dozer/DozerTypeConverterLoader.java | 28 +++++-- .../dozer/ThreadContextClassLoader.java | 83 ++++++++++++++++++++ 4 files changed, 126 insertions(+), 54 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/87eebc6c/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/CamelToDozerClassResolverAdapter.java ---------------------------------------------------------------------- diff --git a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/CamelToDozerClassResolverAdapter.java b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/CamelToDozerClassResolverAdapter.java deleted file mode 100644 index ad913d1..0000000 --- a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/CamelToDozerClassResolverAdapter.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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.camel.converter.dozer; - -import java.net.URL; - -import org.apache.camel.CamelContext; -import org.apache.camel.impl.DefaultClassResolver; -import org.apache.camel.spi.ClassResolver; -import org.dozer.util.DozerClassLoader; - -public final class CamelToDozerClassResolverAdapter implements DozerClassLoader { - - private final ClassResolver classResolver; - - public CamelToDozerClassResolverAdapter() { - // must have a default nor-arg constructor to allow Dozer to work with OSGi - classResolver = new DefaultClassResolver(); - } - - public CamelToDozerClassResolverAdapter(CamelContext camelContext) { - classResolver = camelContext.getClassResolver(); - } - - public Class<?> loadClass(String name) { - return classResolver.resolveClass(name); - } - - public URL loadResource(String name) { - return DozerTypeConverterLoader.loadMappingFile(classResolver, name); - } -} http://git-wip-us.apache.org/repos/asf/camel/blob/87eebc6c/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverter.java ---------------------------------------------------------------------- diff --git a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverter.java b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverter.java index f1ab849..3bc6135 100644 --- a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverter.java +++ b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverter.java @@ -23,6 +23,8 @@ import org.apache.camel.TypeConverter; import org.apache.camel.support.TypeConverterSupport; import org.dozer.DozerBeanMapper; import org.dozer.Mapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * <code>DozerTypeConverter</code> is a standard {@link TypeConverter} that @@ -36,6 +38,8 @@ import org.dozer.Mapper; */ public class DozerTypeConverter extends TypeConverterSupport { + private static final Logger LOG = LoggerFactory.getLogger(DozerTypeConverter.class); + private final DozerBeanMapper mapper; public DozerTypeConverter(DozerBeanMapper mapper) { @@ -48,6 +52,23 @@ public class DozerTypeConverter extends TypeConverterSupport { @Override public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException { - return mapper.map(value, type); + // if the exchange is null, we have no chance to ensure that the TCCL is the one from the CamelContext + if (exchange == null) { + return mapper.map(value, type); + } + + // otherwise, we ensure that the TCCL is the correct one + T answer; + ClassLoader prev = Thread.currentThread().getContextClassLoader(); + ClassLoader contextCl = exchange.getContext().getApplicationContextClassLoader(); + LOG.debug("Switching TCCL to: {}.", contextCl); + try { + Thread.currentThread().setContextClassLoader(contextCl); + answer = mapper.map(value, type); + } finally { + LOG.debug("Restored TCCL to: {}.", prev); + Thread.currentThread().setContextClassLoader(prev); + } + return answer; } } http://git-wip-us.apache.org/repos/asf/camel/blob/87eebc6c/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverterLoader.java ---------------------------------------------------------------------- diff --git a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverterLoader.java b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverterLoader.java index 6118caf..fb40c3b 100644 --- a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverterLoader.java +++ b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/DozerTypeConverterLoader.java @@ -17,6 +17,7 @@ package org.apache.camel.converter.dozer; import java.lang.reflect.Field; + import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -104,16 +105,14 @@ public class DozerTypeConverterLoader extends ServiceSupport implements CamelCon public DozerTypeConverterLoader(CamelContext camelContext, DozerBeanMapperConfiguration configuration) { GlobalSettings settings = GlobalSettings.getInstance(); try { - log.info("Configuring GlobalSettings to use Camel classloader: {}", CamelToDozerClassResolverAdapter.class.getName()); + log.info("Configuring GlobalSettings to use Camel classloader: {}", ThreadContextClassLoader.class.getName()); Field field = settings.getClass().getDeclaredField("classLoaderBeanName"); - ReflectionHelper.setField(field, settings, CamelToDozerClassResolverAdapter.class.getName()); + ReflectionHelper.setField(field, settings, ThreadContextClassLoader.class.getName()); } catch (Exception e) { throw new IllegalStateException("Cannot configure Dozer GlobalSettings to use CamelToDozerClassResolverAdapter as classloader due " + e.getMessage(), e); } - // must set class loader before we create bean mapper - CamelToDozerClassResolverAdapter adapter = new CamelToDozerClassResolverAdapter(camelContext); - BeanContainer.getInstance().setClassLoader(adapter); + switchClassloader(); log.info("Using DozerBeanMapperConfiguration: {}", configuration); DozerBeanMapper mapper = createDozerBeanMapper(configuration); @@ -164,8 +163,7 @@ public class DozerTypeConverterLoader extends ServiceSupport implements CamelCon this.mapper = mapper; } - CamelToDozerClassResolverAdapter adapter = new CamelToDozerClassResolverAdapter(camelContext); - BeanContainer.getInstance().setClassLoader(adapter); + switchClassloader(); Map<String, DozerBeanMapper> mappers = lookupDozerBeanMappers(); // only add if we do not already have it @@ -361,6 +359,22 @@ public class DozerTypeConverterLoader extends ServiceSupport implements CamelCon this.mapper = mapper; } + protected void switchClassloader() { + // must set class loader before we create bean mapper + DozerClassLoader oldCl = BeanContainer.getInstance().getClassLoader(); + log.info("Current Dozer container-wide classloader: {}.", oldCl); + + // if the classloader we're replacing is not a ThreadContextClassLoader, pass it on as delegate target + // otherwise, don't do anything as we have + if (!(oldCl instanceof ThreadContextClassLoader)) { + ThreadContextClassLoader newCl = new ThreadContextClassLoader(oldCl); + BeanContainer.getInstance().setClassLoader(newCl); + log.info("Switched Dozer container-wide classloader from: {} to {} (where the latter delegates to former).", oldCl, newCl); + } else { + log.info("Dozer container-wide classloader already set: {}. No switch necessary.", oldCl); + } + } + protected static URL loadMappingFile(ClassResolver classResolver, String mappingFile) { URL url = null; try { http://git-wip-us.apache.org/repos/asf/camel/blob/87eebc6c/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/ThreadContextClassLoader.java ---------------------------------------------------------------------- diff --git a/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/ThreadContextClassLoader.java b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/ThreadContextClassLoader.java new file mode 100644 index 0000000..44cf055 --- /dev/null +++ b/components/camel-dozer/src/main/java/org/apache/camel/converter/dozer/ThreadContextClassLoader.java @@ -0,0 +1,83 @@ +/** + * 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.camel.converter.dozer; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.dozer.util.DozerClassLoader; +import org.dozer.util.MappingUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ThreadContextClassLoader implements DozerClassLoader { + + private static final Logger LOG = LoggerFactory.getLogger(ThreadContextClassLoader.class); + + private DozerClassLoader delegate; + + public ThreadContextClassLoader(DozerClassLoader delegate) { + this.delegate = delegate; + } + + @Override + public Class<?> loadClass(String className) { + LOG.debug("Loading class from classloader: {}.", Thread.currentThread().getContextClassLoader()); + Class<?> result = null; + try { + // try to resolve the class from the thread context classloader + result = ClassUtils.getClass(Thread.currentThread().getContextClassLoader(), className); + } catch (ClassNotFoundException e) { + // if unresolvable, ask the delegate + result = delegate.loadClass(className); + if (result == null) { + MappingUtils.throwMappingException(e); + } + } + return result; + } + + @Override + public URL loadResource(String uri) { + LOG.debug("Loading resource from classloader: {}.", Thread.currentThread().getContextClassLoader()); + URL answer = Thread.currentThread().getContextClassLoader().getResource(uri); + + // try loading it from the delegate + if (answer == null) { + answer = delegate.loadResource(uri); + } + + // try treating it as a system resource + if (answer == null) { + answer = ClassLoader.getSystemResource(uri); + } + + // one more time in case it's a normal URI + if (answer == null && StringUtils.contains(uri, ":")) { + try { + answer = new URL(uri); + } catch (MalformedURLException e) { + MappingUtils.throwMappingException(e); + } + } + + return answer; + } + +}