blob: e027d7978ce0299c775382980d22f39382663fa1 [file] [log] [blame]
/*
* 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.uima.internal.util;
import java.net.URL;
/**
* Class Loader for loading localized messages
* See https://issues.apache.org/jira/browse/UIMA-1714
* Delegates to other ClassLoaders, in the following order
* the class loader that loaded the 2nd previous caller
* the class loader that loaded the 3rd previous caller
* etc.
* and finally, the thread local context loader, if it exists UIMA-3692
* Note: the caller of this method is presumed to be framework code
* that was, in turn, called to perform some logging or whatever,
* so we skip the 1st previous caller.
*
* Note that each of these class loaders will, in turn, delegate
* if they are set up to do so
*
* Note: if a caller's class loader is the same as the previously tried one,
* we skip it (simple speed optimization, and may avoid some kind of strange loop)
*/
public class MsgLocalizationClassLoader {
/**
* this inner class only for purposes of getting access to the protected method
* to get the call stack
*/
static class CallStack extends SecurityManager {
Class<?>[] getCallStack() {
return getClassContext();
}
}
// static final CallStack csi = new CallStack(); // not used 2/2018
/**
* One instance of this class made
* Must be thread-safe
*/
static class CallClimbingClassLoader extends ClassLoader {
/**
* This value is set / cleared in a try / finally block, from
* the value of originalContextClassLoader in the
* two classes org.apache.uima.InternationalizedRuntimeException and
* org.apache.uima.InternationalizedException
*
* The purpose is to enable using the class loader from the context where the
* exception was created.
*/
static final ThreadLocal<ClassLoader> original_thread_context_class_loader = new ThreadLocal<>();
/*
* Try to load the class itself before delegate the class loading to its parent
*/
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
// try to load class
c = findClass(name); // may throw; if so, don't delegate, delegation has already been done
}
if (resolve) {
resolveClass(c);
}
return c;
}
/**
* Called after findLoadedClass has returned null
* Delegates loading in specific order
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// UIMA-3692, UIMA-4793 try the thread context class loader
// if not found, will return class not found exception
ClassLoader cl = original_thread_context_class_loader.get();
if (cl != null) {
try {
return cl.loadClass(name);
} catch (ClassNotFoundException e) {}
}
// 2nd try: the current thread context class loader
ClassLoader context_classLoader = Thread.currentThread().getContextClassLoader();
if (null != context_classLoader && cl != context_classLoader) {
try {
return context_classLoader.loadClass(name);
} catch (ClassNotFoundException e) {}
}
// get the call stack; "new" is needed to get the current context call stack
ClassLoader[] cls = Misc.getCallingClass_classLoaders();
for (ClassLoader ccl : cls) {
if (ccl == cl || ccl == context_classLoader) {
continue; // alread tried
}
Class<?> c = null;
try {
c = ccl.loadClass(name); // include delegation
return c;
} catch (ClassNotFoundException e) {}
} // end of for loop, looking through all the super classes
throw new ClassNotFoundException(name);
}
@Override
public URL getResource(String name) {
// UIMA-3692, UIMA-4793 try the thread context class loader
// if not found, will return class not found exception
ClassLoader cl = original_thread_context_class_loader.get();
if (cl != null) {
URL c = cl.getResource(name);
if (null != c) {
return c;
}
}
// 2nd try: the current thread context class loader
ClassLoader context_classLoader = Thread.currentThread().getContextClassLoader();
if (null != context_classLoader && cl != context_classLoader) {
URL c = context_classLoader.getResource(name);
if (null != c) {
return c;
}
}
ClassLoader[] cls = Misc.getCallingClass_classLoaders();
for (ClassLoader ccl : cls) {
if (ccl == cl || ccl == context_classLoader) {
continue; // alread tried
}
URL c = ccl.getResource(name);
if (null != c) {
return c;
}
} // end of for loop, looking through all the super classes
return null;
// Map<ClassLoader,ClassLoader> alreadySearched = new IdentityHashMap<>(7);
// // get the call stack
// Class<?>[] cs = new CallStack().getCallStack();
// // start at the caller of the caller's class loader
// // cs[0] is getClassContext
// // cs[1] is getCallStack
// // cs[2] is this method, find class
// for (int i = 3; i < cs.length; i++) {
// Class<?> callingClass = cs[i];
// ClassLoader cl = callingClass.getClassLoader();
// if (null == cl) { // means system class loader
// cl = ClassLoader.getSystemClassLoader();
// }
// if (null != alreadySearched.get(cl)) {
// continue;
// }
// alreadySearched.put(cl, cl);
//
// URL c = cl.getResource(name); // include delegation
// if (null != c) {
// return c;
// }
// }
// // UIMA-3692, UIMA-4793 try the thread context class loader
// // if not found, will return class not found exception
// ClassLoader cl = original_thread_context_class_loader.get();
// if (cl != null) {
// URL c = cl.getResource(name);
// if (null != c) {
// return c;
// }
// }
// return Thread.currentThread().getContextClassLoader().getResource(name);
// }
}
}
private static final CallClimbingClassLoader MSG_LOCALIZATION_CLASS_LOADER =
new CallClimbingClassLoader();
public static URL getResource(String name) {
return MSG_LOCALIZATION_CLASS_LOADER.getResource(name);
}
public static ClassLoader getMsgLocalizationClassLoader() {
return MSG_LOCALIZATION_CLASS_LOADER;
}
public static Class<?> loadClass(String name) throws ClassNotFoundException {
return MSG_LOCALIZATION_CLASS_LOADER.loadClass(name);
}
}