package org.red5.server; /* * RED5 Open Source Flash Server - http://www.osflash.org/red5 * * Copyright (c) 2006-2008 by respective authors (see below). All rights reserved. * * This library is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any later * version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; /** * Class used to get the Servlet Class loader. The class loader returned is a * child first class loader. * * <br /> * <i>This class is based on original code from the XINS project, by * Anthony Goubard (anthony.goubard@japplis.com)</i> * * @author Paul Gregoire (mondain@gmail.com) */ public final class ServletClassLoader { private static Logger log = Red5LoggerFactory.getLogger(ServletClassLoader.class); /** * Use the current class loader to load the servlet and the libraries. */ public static final int USE_CURRENT_CLASSPATH = 1; /** * Load the Servlet code from the WAR file and use the current classpath for * the libraries. */ public static final int USE_CLASSPATH_LIB = 2; /** * Load the servlet code from the WAR file and try to find the libraries in * the common red5 lib directory. */ public static final int USE_RED5_LIB = 3; /** * Load the servlet code and the libraries from the WAR file. This may take * some time as the libraries need to be extracted from the WAR file. */ public static final int USE_WAR_LIB = 4; /** * Load the servlet code and the standard libraries from the CLASSPATH. Load * the included external libraries from the WAR file. */ public static final int USE_WAR_EXTERNAL_LIB = 5; /** * Gest the class loader that will loader the servlet. * * @param warFile * the WAR file containing the Servlet. * * @param mode * the mode in which the servlet should be loaded. The possible * values are <code>USE_CURRENT_CLASSPATH</code>, * <code>USE_CLASSPATH_LIB</code>, <code>USE_XINS_LIB</code>, * <code>USE_WAR_LIB</code>, <code>USE_WAR_EXTERNAL_LIB</code>. * * @return the Class loader to use to load the Servlet. * * @throws IOException * if the file cannot be read or is incorrect. */ public static ClassLoader getServletClassLoader(File warFile, int mode) throws IOException { if (mode == USE_CURRENT_CLASSPATH) { return ServletClassLoader.class.getClassLoader(); } List<URL> urlList = new ArrayList<URL>(13); if (warFile != null) { // Add the WAR file so that it can locate web pages included in the WAR // file urlList.add(warFile.toURI().toURL()); if (mode != USE_WAR_EXTERNAL_LIB) { URL classesURL = new URL("jar:file:" + warFile.getAbsolutePath().replace(File.separatorChar, '/') + "!/WEB-INF/classes/"); urlList.add(classesURL); } } List<String> standardLibs = new ArrayList<String>(7); if (mode == USE_RED5_LIB) { //get red5 lib system property, if not found build it String libPath = System.getProperty("red5.lib_root"); if (libPath == null) { // look for red5 home as a system property String home = System.getProperty("red5.root"); // if home is null check environmental if (home == null) { //check for env variable home = System.getenv("RED5_HOME"); } //if home is null or equal to "current" directory if (home == null || ".".equals(home)) { //if home is still null look it up via this classes loader String classLocation = ServletClassLoader.class .getProtectionDomain().getCodeSource().getLocation() .toString(); System.out.printf("Classloader location: %s\n", classLocation); //snip off anything beyond the last slash home = classLocation.substring(0, classLocation.lastIndexOf('/')); } //construct the lib path libPath = home + File.separatorChar + "lib"; } System.out.printf("Library path: %s\n", libPath); //grab the urls for all the jars in "lib" File libDir = new File(libPath); File[] libFiles = libDir.listFiles(); for (int i = 0; i < libFiles.length; i++) { if (libFiles[i].getName().endsWith(".jar")) { urlList.add(libFiles[i].toURI().toURL()); } } } if (mode == USE_CLASSPATH_LIB || mode == USE_WAR_EXTERNAL_LIB) { String classPath = System.getProperty("java.class.path"); StringTokenizer stClassPath = new StringTokenizer(classPath, File.pathSeparator); while (stClassPath.hasMoreTokens()) { String nextPath = stClassPath.nextToken(); if (nextPath.toLowerCase().endsWith(".jar")) { standardLibs.add(nextPath.substring(nextPath .lastIndexOf(File.separatorChar) + 1)); } urlList.add(new File(nextPath).toURI().toURL()); } } if (mode == USE_WAR_LIB || mode == USE_WAR_EXTERNAL_LIB) { if (warFile.isDirectory()) { File libDir = new File(warFile, "WEB-INF/lib"); //this should not be null but it can happen if (libDir != null && libDir.canRead()) { File[] libs = libDir.listFiles(); log.debug("Webapp lib count: {}", libs.length); for (File lib : libs) { urlList.add(lib.toURI().toURL()); } } } else { JarInputStream jarStream = new JarInputStream(new FileInputStream(warFile)); JarEntry entry = jarStream.getNextJarEntry(); while (entry != null) { String entryName = entry.getName(); if (entryName.startsWith("WEB-INF/lib/") && entryName.endsWith(".jar") && !standardLibs.contains(entryName.substring(12))) { File tempJarFile = unpack(jarStream, entryName); urlList.add(tempJarFile.toURI().toURL()); } entry = jarStream.getNextJarEntry(); } jarStream.close(); } } URL[] urls = new URL[urlList.size()]; for (int i = 0; i < urlList.size(); i++) { urls[i] = (URL) urlList.get(i); } //set the url classloader as the parent ClassLoader loader = new ChildFirstClassLoader(urls, Thread.currentThread().getContextClassLoader()); //ClassLoader loader = new ChildFirstClassLoader(urls, ServletClassLoader.class.getClassLoader()); //Thread.currentThread().setContextClassLoader(loader); //System.out.printf("Classloaders:\nThread %s\nServlet Loader %s\nChildFirst %s\n", Thread.currentThread().getContextClassLoader(), ServletClassLoader.class.getClassLoader(), loader); return loader; } /** * Unpack the specified entry from the JAR file. * * @param jarStream * The input stream of the JAR file positioned at the entry. * @param entryName * The name of the entry to extract. * * @return The extracted file. The created file is a temporary file in the * temporary directory. * * @throws IOException * if the JAR file cannot be read or is incorrect. */ private static File unpack(JarInputStream jarStream, String entryName) throws IOException { String libName = entryName.substring(entryName.lastIndexOf('/') + 1, entryName.length() - 4); File tempJarFile = File.createTempFile("tmp_" + libName, ".jar"); tempJarFile.deleteOnExit(); FileOutputStream out = new FileOutputStream(tempJarFile); // Transfer bytes from the JAR file to the output file byte[] buf = new byte[8192]; int len; while ((len = jarStream.read(buf)) > 0) { out.write(buf, 0, len); } out.close(); return tempJarFile; } }