/*==========================================================================*\ | $Id: JarSubsystem.java,v 1.2 2011/03/07 18:44:37 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2011 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT 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 General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.core; import com.webobjects.appserver.*; import com.webobjects.foundation.*; import java.io.*; import java.net.*; import java.util.jar.*; import java.lang.reflect.Constructor; import org.webcat.core.DelegatingUrlClassLoader; import org.webcat.core.JarSubsystem; import org.webcat.core.Subsystem; import org.apache.log4j.Logger; // ------------------------------------------------------------------------- /** * Represents a Web-CAT Subsystem maintained within a JAR file. All * jar-based subsystems should inherit from this base to get access * to jar-oriented functions. Such classes should only implement * a single constructor corresponding to (and calling) the * <code>JarSubsystem(JarFile)</code> constructor in this class. * * @author lally * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.2 $, $Date: 2011/03/07 18:44:37 $ */ public abstract class JarSubsystem extends Subsystem { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Initialize a subsystem from a jar file. * * @param jarFile The file that contains the subsystem being loaded */ public JarSubsystem(JarFile jarFile) { super(); this.jarFile = jarFile; } //~ Methods ............................................................... // ---------------------------------------------------------- /** * Initialize a subsystem from a jar file. * * @param jarFile The file that contains the subsystem to load * @return the new subsystem object * @throws IOException */ @SuppressWarnings("deprecation") static public JarSubsystem initializeSubsystemFromJar(File jarFile) throws IOException { JarFile file = new JarFile(jarFile); URL u = jarFile.toURL(); JarSubsystem result = null; log.info("Loading subsystem: " + u.toString()); DelegatingUrlClassLoader.getClassLoader().addURL(u); Class<?> subsysClass = loadMainClass(file); if (subsysClass != null) { try { Constructor<?> constructor = subsysClass.getConstructor( new Class[] { JarFile.class } ); result = (JarSubsystem)constructor.newInstance( new Object[] { file } ); } catch (Exception e) { log.error("Exception loading subsystem: ", e); } } return result; } // ---------------------------------------------------------- /** * Get the subsystem's main class. * * @param jarFile the jar file from which to retrieve the main class * @return The class that represents the subsystem */ static public Class<?> loadMainClass(JarFile jarFile) { try { String mainClass = jarFile.getManifest().getMainAttributes().getValue( Attributes.Name.MAIN_CLASS); return DelegatingUrlClassLoader.getClassLoader().loadClass( mainClass); } catch (Exception e) { log.error("Exception loading subsystem: ", e); } return null; } // ---------------------------------------------------------- /** * Get the jar file for this subsystem. * * @return The jar file containing the subsystem */ public JarFile jarFile() { return jarFile; } // ---------------------------------------------------------- /** * Get a file ("entry") that resides within the subsystem's JAR file. * * @param entry The file to get * @return An InputStream to the requested entry * @throws IOException If an error occurs getting the entry */ public InputStream jarFileEntry(String entry) throws IOException { return jarFile.getInputStream(jarFile.getEntry(entry)); } // ---------------------------------------------------------- /** * Returns a mimetype for a given pathname. This is only valid for * <b>image</b> files. Everything else returns <code>null</code>. * TODO: integrate this with the primary MIME decoding logic elsewhere * in the project. * * @param path The path to test * @return The MIME type of the given path */ public static String mimeTypeForPath(String path) { if (path.endsWith(".jpg")) { return "image/jpeg"; } else if (path.endsWith(".gif")) { return "image/gif"; } else if (path.endsWith(".png")) { return "image/png"; } else { return null; } } // ---------------------------------------------------------- /** * Returns whether or not the given path is safe to return. This denies * the downloading of files other than those that should be. Example: * .class files shouldn't be downloaded, but .gif files should. * * @param path The path inside the jar to be verified * @return True if the path describes an allowed file type */ public static boolean pathIsSafeToReturn(String path) { return mimeTypeForPath(path) != null; } // ---------------------------------------------------------- /** * Returns a WOResponse containing the data from the given jarpath, * which contains the file <b>iff</b> the path is safe, as determined * by {@link #pathIsSafeToReturn(String)}. * * @param url The path of the file within the JAR. Example: * /images/foo.gif * @return The WOResponse to display to the user */ public WOResponse jarResponseFromUrl(String url) { WOResponse response = new WOResponse(); if (pathIsSafeToReturn(url)) { try { // TODO: check to see if this leaks file handles or other // resources! response.setContent( new NSData(jarFileEntry(url), CHUNK_SIZE)); response.setHeader( "Content-Type", mimeTypeForPath(url)); } catch (Exception e) { response.setStatus(WOMessage.HTTP_STATUS_NOT_FOUND); e.printStackTrace(); } } else { response.setStatus(WOMessage.HTTP_STATUS_FORBIDDEN); } return response; } //~ Instance/static variables ............................................. private static int CHUNK_SIZE = 16384; /** The jar file containing this subsystem. */ private JarFile jarFile = null; static Logger log = Logger.getLogger(JarSubsystem.class); }