Fields Summary |
---|
private static boolean | isInitializedrecords whether initialization has been completed |
private static final String | URLCLASSLOADER_UCP_FIELD_NAMEnames of classes and fields of interest for closing the loader's jar files |
private static final String | URLCLASSPATH_LOADERS_FIELD_NAME |
private static final String | URLCLASSPATH_URLS_FIELD_NAME |
private static final String | URLCLASSPATH_LMAP_FIELD_NAME |
private static final String | URLCLASSPATH_JARLOADER_INNER_CLASS_NAME |
private static final String | URLCLASSPATH_JARLOADER_JARFILE_FIELD_NAME |
private static Field | jcpField |
private static Field | loadersField |
private static Field | urlsField |
private static Field | lmapField |
private static Class | jarLoaderInnerClass |
private static Field | jarFileField |
private static boolean | initDone |
Methods Summary |
---|
private static java.lang.reflect.Field | getField(java.lang.Class cls, java.lang.String fieldName)Retrieves a Field object for a given field on the specified class, having
set it accessible.
try {
Field field = cls.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException nsfe) {
NoSuchFieldException e = new NoSuchFieldException(getMessage("classloaderutil.errorGettingField", fieldName));
e.initCause(nsfe);
throw e;
}
|
private static java.lang.Class | getInnerClass(java.lang.Class cls, java.lang.String innerClassName)Retrieves a given inner class definition from the specified outer class.
Class result = null;
Class[] innerClasses = cls.getDeclaredClasses();
for (Class c : innerClasses) {
if (c.getName().equals(innerClassName)) {
result = c;
break;
}
}
return result;
|
private static java.util.logging.Logger | getLogger()Returns the logger for the common utils component.
return LogDomains.getLogger(LogDomains.CMN_LOGGER);
|
private static java.lang.String | getMessage(java.lang.String key, java.lang.Object o)Returns a formatted string, using the key to find the full message and
substituting any parameters.
String msg = getLogger().getResourceBundle().getString(key);
return MessageFormat.format(msg, o);
|
private static void | init()Initializes the class.
Each utility method should invoke init() before doing their own work
to make sure the initialization is done.
if ( ! initDone) {
initForClosingJars();
initDone = true;
}
|
private static void | initForClosingJars()Sets up variables used in closing a loader's jar files.
jcpField = getField(URLClassLoader.class, URLCLASSLOADER_UCP_FIELD_NAME);
loadersField = getField(URLClassPath.class, URLCLASSPATH_LOADERS_FIELD_NAME);
urlsField = getField(URLClassPath.class, URLCLASSPATH_URLS_FIELD_NAME);
lmapField = getField(URLClassPath.class, URLCLASSPATH_LMAP_FIELD_NAME);
jarLoaderInnerClass = getInnerClass(URLClassPath.class, URLCLASSPATH_JARLOADER_INNER_CLASS_NAME);
jarFileField = getField(jarLoaderInnerClass, URLCLASSPATH_JARLOADER_JARFILE_FIELD_NAME);
|
public static void | releaseLoader(java.net.URLClassLoader classLoader)Releases resources held by the URLClassLoader. Notably, close the jars
opened by the loader. This does not prevent the class loader from
continuing to return classes it has already resolved.
releaseLoader(classLoader, null);
|
public static java.io.IOException[] | releaseLoader(java.net.URLClassLoader classLoader, java.util.Vector jarsClosed)Releases resources held by the URLClassLoader. Notably, close the jars
opened by the loader. This does not prevent the class loader from
continuing to return classes it has alrady resolved although that is not
what we intend to happen. Initializes and updates the Vector of
jars that have been successfully closed.
Any errors are logged.
IOException[] result = null;
try {
init();
/* Records all IOExceptions thrown while closing jar files. */
Vector<IOException> ioExceptions = new Vector<IOException>();
if (jarsClosed != null) {
jarsClosed.clear();
}
URLClassPath ucp = (URLClassPath) jcpField.get(classLoader);
ArrayList loaders = (ArrayList) loadersField.get(ucp);
Stack urls = (Stack) urlsField.get(ucp);
HashMap lmap = (HashMap) lmapField.get(ucp);
/*
*The urls variable in the URLClassPath object holds URLs that have not yet
*been used to resolve a resource or load a class and, therefore, do
*not yet have a loader associated with them. Clear the stack so any
*future requests that might incorrectly reach the loader cannot be
*resolved and cannot open a jar file after we think we've closed
*them all.
*/
synchronized(urls) {
urls.clear();
}
/*
*Also clear the map of URLs to loaders so the class loader cannot use
*previously-opened jar files - they are about to be closed.
*/
synchronized(lmap) {
lmap.clear();
}
/*
*The URLClassPath object's path variable records the list of all URLs that are on
*the URLClassPath's class path. Leave that unchanged. This might
*help someone trying to debug why a released class loader is still used.
*Because the stack and lmap are now clear, code that incorrectly uses a
*the released class loader will trigger an exception if the
*class or resource would have been resolved by the class
*loader (and no other) if it had not been released.
*
*The list of URLs might provide some hints to the person as to where
*in the code the class loader was set up, which might in turn suggest
*where in the code the class loader needs to stop being used.
*The URLClassPath does not use the path variable to open new jar
*files - it uses the urls Stack for that - so leaving the path variable
*will not by itself allow the class loader to continue handling requests.
*/
/*
*For each loader, close the jar file associated with that loader.
*
*The URLClassPath's use of loaders is sync-ed on the entire URLClassPath
*object.
*/
synchronized (ucp) {
for (Object o : loaders) {
if (o != null) {
/*
*If the loader is a JarLoader inner class and its jarFile
*field is non-null then try to close that jar file. Add
*it to the list of closed files if successful.
*/
if (jarLoaderInnerClass.isInstance(o)) {
try {
JarFile jarFile = (JarFile) jarFileField.get(o);
try {
if (jarFile != null) {
jarFile.close();
if (jarsClosed != null) {
jarsClosed.add(jarFile.getName());
}
}
} catch (IOException ioe) {
/*
*Wrap the IOException to identify which jar
*could not be closed and add it to the list
*of IOExceptions to be returned to the caller.
*/
String jarFileName = (jarFile == null) ? getMessage("classloaderutil.jarFileNameNotAvailable") : jarFile.getName();
String msg = getMessage("classloaderutil.errorClosingJar", jarFileName);
IOException newIOE = new IOException(msg);
newIOE.initCause(ioe);
ioExceptions.add(newIOE);
/*
*Log the error also.
*/
getLogger().log(Level.WARNING, msg, ioe);
}
} catch (Throwable thr) {
getLogger().log(Level.WARNING, "classloaderutil.errorReleasingJarNoName", thr);
}
}
}
}
/*
*Now clear the loaders ArrayList.
*/
loaders.clear();
}
result = ioExceptions.toArray(new IOException[ioExceptions.size()]);
} catch (Throwable thr) {
getLogger().log(Level.WARNING, "classloaderutil.errorReleasingLoader", thr);
result = null;
}
return result;
|