Java tutorial
/* * Copyright 2002-2004 Greg Hinkle * * Licensed 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.mc4j.ems.connection.support.classloader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mc4j.ems.connection.ConnectionFactory; import org.mc4j.ems.connection.EmsConnectException; import org.mc4j.ems.connection.EmsException; import org.mc4j.ems.connection.settings.ConnectionSettings; import org.mc4j.ems.connection.support.metadata.JSR160ConnectionTypeDescriptor; import org.mc4j.ems.connection.support.metadata.LocalVMTypeDescriptor; /** * @author Greg Hinkle (ghinkle@users.sourceforge.net), Apr 5, 2005 * @version $Revision: 629 $($Author: ianpspringer $ / $Date: 2011-10-28 23:44:26 +0200 (Fr, 28 Okt 2011) $) */ public class ClassLoaderFactory { private static ClassLoaderFactory INSTANCE; private static Log log = LogFactory.getLog(ClassLoaderFactory.class); private static Map<String, File> jarCache = new HashMap<String, File>(); private static Map<FileKey, File> tempJarCache = Collections.synchronizedMap(new HashMap<FileKey, File>()); private static Map<Long, WeakReference<ClassLoader>> classLoaderCache = Collections .synchronizedMap(new HashMap<Long, WeakReference<ClassLoader>>()); static { String className = System.getProperty("org.mc4j.ems.classloaderfactory"); if (className != null) { try { INSTANCE = ((Class<ClassLoaderFactory>) Class.forName(className)).newInstance(); } catch (Exception e) { throw new EmsException("Unable to load custom classloader factory " + className, e); } } if (INSTANCE == null) { INSTANCE = new ClassLoaderFactory(); } } /** * Retrieves the configured classloader factory for EMS. This can be customized by * setting the system property "org.mc4j.ems.classloaderfactory". * * @return the Classloader Factory used to build the connection classloader */ public static ClassLoaderFactory getInstance() { return INSTANCE; } /** * Clears this factory's caches. You usually only call this when you need * help cleaning out the classloaders created by this factory. */ public static void clearCaches() { jarCache.clear(); tempJarCache.clear(); classLoaderCache.clear(); } /** * TODO GH: Implement a special classloader that can load classes from * within a jar inside another jar or perhaps just ship the impl jar separately... */ protected URL storeImplToTemp(String archiveResource, File tempDir) { try { if (jarCache.containsKey(archiveResource)) { return jarCache.get(archiveResource).toURI().toURL(); } InputStream is = ClassLoaderFactory.class.getClassLoader().getResourceAsStream(archiveResource); if (is == null) { throw new EmsException("Unable to find resource to store [" + archiveResource + "]"); } // String tmpPath = System.getProperty("java.io.tmpdir"); File tmpFile = copyFileToTemp(archiveResource, is, tempDir); jarCache.put(archiveResource, tmpFile); return tmpFile.toURI().toURL(); } catch (FileNotFoundException e) { throw new EmsException("Unable to store jar [" + archiveResource + "] to temp dir [" + tempDir + "].", e); } catch (IOException e) { throw new EmsException("Unable to store jar [" + archiveResource + "] to temp dir [" + tempDir + "].", e); } } private File copyFileToTemp(String archiveResource, InputStream is, File directory) throws IOException { String jarName = new File(archiveResource).getName(); jarName = jarName.substring(0, jarName.length() - 4); File tmpFile = File.createTempFile(jarName, ".jar", directory); tmpFile.deleteOnExit(); log.trace("Copying jar [" + archiveResource + "] to temporary file [" + tmpFile.getAbsolutePath() + "]"); FileOutputStream fos = new FileOutputStream(tmpFile); byte[] buffer = new byte[4096]; int size = is.read(buffer); while (size != -1) { fos.write(buffer, 0, size); size = is.read(buffer); } fos.close(); is.close(); return tmpFile; } /* private static String digest(String algorithm, ByteBuffer buffer) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance(algorithm); md.update(buffer.duplicate()); byte[] digest = md.digest(); StringBuilder result = new StringBuilder(); for (int i = 0; i < digest.length; i++) { result.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1)); } return result.toString(); // new Formatter(System.out).format // ("%-5s: %0" + (digest.length * 2) + "x%n", // algorithm, new BigInteger(1, digest)); } */ public URL getCachedTempForFile(File file, File directory) throws MalformedURLException { try { FileKey key = new FileKey(file); File result = tempJarCache.get(key); if (result == null) { result = copyFileToTemp(file.getName(), new FileInputStream(file), directory); tempJarCache.put(key, result); } return result.toURI().toURL(); // long modified = file.lastModified(); // file.hashCode() // FileChannel channel = new RandomAccessFile(file, "r").getChannel(); // file.le // ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, (int) channel.size()); // digest("md5", buffer); // channel.close(); } catch (IOException ioe) { log.debug("Could not create temporary copy of jar [" + file + "]", ioe); return file.toURI().toURL(); // Will have to refer directly to the file since we couldn't make a temp copy } } public ClassLoader buildClassLoader(ConnectionSettings settings) { String tempDirString = (String) settings.getControlProperties().get(ConnectionFactory.JAR_TEMP_DIR); File tempDir = null; if (tempDirString != null) { tempDir = new File(tempDirString); } Boolean useContextClassLoader = Boolean.valueOf( settings.getAdvancedProperties().getProperty(ConnectionFactory.USE_CONTEXT_CLASSLOADER, "false")); if (useContextClassLoader.booleanValue()) { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); URL implURL = storeImplToTemp("org-mc4j-ems-impl.jar", tempDir); ClassLoader loader = new URLClassLoader(new URL[] { implURL }, contextClassLoader); return loader; } List<URL> entries = new ArrayList<URL>(); if (settings.getClassPathEntries() != null) { for (File file : settings.getClassPathEntries()) { try { if (Boolean.valueOf(settings.getControlProperties() .getProperty(ConnectionFactory.COPY_JARS_TO_TEMP, "false"))) { entries.add(getCachedTempForFile(file, tempDir)); } else { entries.add(file.toURI().toURL()); } } catch (MalformedURLException e) { throw new EmsConnectException("Unable to read class path library url", e); } } } // Now load in the implementation jar // URL implURL = new URL(null, "deepjar://org-mc4j-ems-impl.jar", new Handler()); URL implURL = storeImplToTemp("org-mc4j-ems-impl.jar", tempDir); entries.add(implURL); if (settings.getConnectionType() instanceof LocalVMTypeDescriptor) { // Need tools.jar if its not already loaded try { Class.forName("com.sun.tools.attach.VirtualMachine"); } catch (ClassNotFoundException e) { // Try to load tools.jar File toolsJar = null; toolsJar = findToolsJarForHome(System.getProperty("java.home")); if (toolsJar == null) { toolsJar = findToolsJarForHome(System.getProperty("env_java_home")); } if (toolsJar != null) { try { log.debug("Found tools.jar at " + toolsJar.getPath()); entries.add(toolsJar.toURI().toURL()); } catch (MalformedURLException e1) { /* Unnexpected */ } } else { throw new EmsConnectException( "Unable to find tools.jar. Add it to your classpath to use Sun local vm connections."); } } } // Add internal support jars for JSR160 on < jdk5 if ((settings.getConnectionType() instanceof JSR160ConnectionTypeDescriptor) && settings.getConnectionType().getConnectionClasspathEntries() == null && Double.parseDouble(System.getProperty("java.version").substring(0, 3)) < 1.5) { entries.add(storeImplToTemp("lib/jsr160-includes/mx4j.jar", tempDir)); entries.add(storeImplToTemp("lib/jsr160-includes/mx4j-remote.jar", tempDir)); } // TODO: Check if file exists, log warning if not URL[] entryArray = entries.toArray(new URL[entries.size()]); ClassLoader loader = null; long key = Arrays.hashCode(entryArray); WeakReference<ClassLoader> loaderReference = classLoaderCache.get(key); if (loaderReference != null) { loader = classLoaderCache.get(key).get(); } if (loader == null) { // WARNING: Relatively disgusting hack. hiding classes is not a good thing if (settings.getConnectionType().isUseChildFirstClassLoader()) { loader = new ChildFirstClassloader(entryArray, ClassLoaderFactory.class.getClassLoader()); } else { // TODO was NestedJarClassLoader //loader = new ChildFirstClassloader(entryArray, ClassLoaderFactory.class.getClassLoader()); loader = new URLClassLoader(entryArray, ClassLoaderFactory.class.getClassLoader()); //loader = new NestedJarClassLoader(entryArray, ClassLoaderFactory.class.getClassLoader()); } classLoaderCache.put(key, new WeakReference<ClassLoader>(loader)); if (log.isDebugEnabled()) { StringBuffer buf = new StringBuffer("Classloader built with: \n"); for (URL url : entries) { buf.append("\t").append(url).append("\n"); } log.info(buf.toString()); } } return loader; } private File findToolsJarForHome(String javaHome) { File toolsJar = null; if (javaHome != null) { File javaHomeDir = new File(javaHome); if (!javaHomeDir.exists() || !javaHomeDir.isDirectory()) return null; toolsJar = findToolsJar(javaHomeDir); if (toolsJar == null) { toolsJar = findToolsJar(javaHomeDir.getParentFile()); } } return toolsJar; } public File findToolsJar(File home) { File f = new File(home, "lib" + File.separator + "tools.jar"); log.debug("Looking for tools jar at: " + f.getPath()); return f.exists() && f.isFile() ? f : null; } public static class FileKey { long lastModified; long size; File f; public FileKey(File f) { this.lastModified = f.lastModified(); this.size = f.length(); this.f = f; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FileKey fileKey = (FileKey) o; if (lastModified != fileKey.lastModified) return false; if (size != fileKey.size) return false; if (!f.equals(fileKey.f)) return false; return true; } public int hashCode() { int result; result = (int) (lastModified ^ (lastModified >>> 32)); result = 31 * result + (int) (size ^ (size >>> 32)); result = 31 * result + f.hashCode(); return result; } } }