You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@locus.apache.org on 2000/08/12 00:46:01 UTC
cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources Constants.java DirectoryBean.java FileResources.java JarResources.java LocalStrings.properties ResourceBean.java ResourcesBase.java package.html
craigmcc 00/08/11 15:46:01
Added: catalina/src/share/org/apache/catalina/resources
Constants.java DirectoryBean.java
FileResources.java JarResources.java
LocalStrings.properties ResourceBean.java
ResourcesBase.java package.html
Log:
Migrate to Tomcat 4.0.
Revision Changes Path
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/Constants.java
Index: Constants.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/Constants.java,v 1.1 2000/08/11 22:45:57 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/08/11 22:45:57 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.resources;
public class Constants {
public static final String Package = "org.apache.catalina.resources";
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/DirectoryBean.java
Index: DirectoryBean.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/DirectoryBean.java,v 1.1 2000/08/11 22:45:57 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/08/11 22:45:57 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.resources;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import org.apache.catalina.util.StringManager;
/**
* Abstraction bean that represents the properties of a "resource" that is
* actually a "directory", in a fashion that independent of the actual
* underlying medium used to represent those entries. Convenient constructors
* are provided to populate our properties from common sources, but it is
* feasible to do everything with property setters if necessary.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that access to the
* set of resources associated with this directory are done in a thread safe
* manner. No internal synchronization is performed.
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/08/11 22:45:57 $
*/
public final class DirectoryBean extends ResourceBean {
// ----------------------------------------------------------- Constructors
/**
* Construct a new directory bean for the named resource, with default
* properties.
*
* @param name Normalized context-relative name of this resource
*/
public DirectoryBean(String name) {
super(name);
}
/**
* Construct a new directory bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param file File representing this resource entry
*/
public DirectoryBean(String name, File file) {
super(name, file);
}
/**
* Construct a new directory bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param entry JAR entry representing this resource entry
*/
public DirectoryBean(String name, JarEntry entry) {
super(name, entry);
}
// ----------------------------------------------------- Instance Variables
/**
* The system default Locale.
*/
private static Locale defaultLocale = Locale.getDefault();
/**
* The date format pattern for rendering last modified date and time.
*/
private static String pattern = "EEE, dd MMM yyyy HH:mm z";
/**
* The collection of resources for this directory, sorted by name.
*/
private TreeMap resources = new TreeMap();
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The date format for rendering last modified date and time.
*/
private static DateFormat timestamp =
new SimpleDateFormat(pattern, defaultLocale);
// --------------------------------------------------------- Public Methods
/**
* Add a new resource to this directory.
*
* @param entry ResourceBean for the resource to be added
*/
public void addResource(ResourceBean resource) {
resources.put(resource.getName(), resource);
}
/**
* Return the set of resources that belong to this directory,
* in alphabetical order based on their names.
*/
public ResourceBean[] findResources() {
ResourceBean results[] = new ResourceBean[resources.size()];
return ((ResourceBean[]) resources.values().toArray(results));
}
/**
* Remove an existing resource from this directory.
*
* @param entry ResourceBean for the resource to be removed
*/
public void removeResource(ResourceBean resource) {
resources.remove(resource.getName());
}
/**
* Return an InputStream to an HTML representation of the contents
* of this directory.
*
* @param contextPath Context path to which our internal paths are
* relative
*/
public InputStream render(String contextPath) {
// Number of characters to trim from the beginnings of filenames
int trim = name.length() + 1;
if (name.equals("/"))
trim = 1;
// Prepare a writer to a buffered area
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(stream);
// FIXME - Currently pays no attention to the user's Locale
// Render the page header
writer.print("<html>\r\n");
writer.print("<head>\r\n");
writer.print("<title>");
writer.print(sm.getString("directory.title", name));
writer.print("</title>\r\n</head>\r\n");
writer.print("<body bgcolor=\"white\">\r\n");
writer.print("<table width=\"90%\" cellspacing=\"0\"" +
" cellpadding=\"5\" align=\"center\">\r\n");
// Render the in-page title
writer.print("<tr><td colspan=\"3\"><font size=\"+2\">\r\n<strong>");
writer.print(sm.getString("directory.title", name));
writer.print("</strong>\r\n</font></td></tr>\r\n");
// Render the link to our parent (if required)
String parentDirectory = name;
if (parentDirectory.endsWith("/")) {
parentDirectory =
parentDirectory.substring(0, parentDirectory.length() - 1);
}
int slash = parentDirectory.lastIndexOf("/");
if (slash >= 0) {
String parent = name.substring(0, slash);
writer.print("<tr><td colspan=\"3\" bgcolor=\"#ffffff\">\r\n");
writer.print("<a href=\"");
writer.print(rewriteUrl(contextPath));
if (parent.equals(""))
parent = "/";
writer.print(parent);
writer.print("\">");
writer.print(sm.getString("directory.parent", parent));
writer.print("</a>\r\n");
writer.print("</td></tr>\r\n");
}
// Render the column headings
writer.print("<tr bgcolor=\"#cccccc\">\r\n");
writer.print("<td align=\"left\"><font size=\"+1\"><strong>");
writer.print(sm.getString("directory.filename"));
writer.print("</strong></font></td>\r\n");
writer.print("<td align=\"center\"><font size=\"+1\"><strong>");
writer.print(sm.getString("directory.size"));
writer.print("</strong></font></td>\r\n");
writer.print("<td align=\"right\"><font size=\"+1\"><strong>");
writer.print(sm.getString("directory.lastModified"));
writer.print("</strong></font></td>\r\n");
writer.print("</tr>\r\n");
// Render the directory entries within this directory
ResourceBean resources[] = findResources();
boolean shade = false;
for (int i = 0;i < resources.length; i++) {
String trimmed = resources[i].getName().substring(trim);
if (trimmed.equalsIgnoreCase("WEB-INF") ||
trimmed.equalsIgnoreCase("META-INF"))
continue;
writer.print("<tr");
if (shade)
writer.print(" bgcolor=\"eeeeee\"");
writer.print(">\r\n");
shade = !shade;
writer.print("<td align=\"left\"> \r\n");
writer.print("<a href=\"");
writer.print(rewriteUrl(contextPath));
writer.print(rewriteUrl(resources[i].getName()));
writer.print("\"><tt>");
writer.print(trimmed);
if (resources[i] instanceof DirectoryBean)
writer.print("/");
writer.print("</tt></a></td>\r\n");
writer.print("<td align=\"right\"><tt>");
if (resources[i] instanceof DirectoryBean)
writer.print(" ");
else
writer.print(renderSize(resources[i].getSize()));
writer.print("</tt></td>\r\n");
writer.print("<td align=\"right\"><tt>");
writer.print(renderLastModified(resources[i].getLastModified()));
writer.print("</tt></td>\r\n");
writer.print("</tr>\r\n");
}
// Render the page footer
writer.print("<tr><td colspan=\"3\"> </td></tr>\r\n");
writer.print("<tr><td colspan=\"3\" bgcolor=\"#cccccc\">");
writer.print("<font size=\"-1\">");
writer.print(sm.getString("directory.version"));
writer.print("</font></td></tr>\r\n");
writer.print("</table>\r\n");
writer.print("</body>\r\n");
writer.print("</html>\r\n");
// Return an input stream to the underlying bytes
writer.flush();
return (new ByteArrayInputStream(stream.toByteArray()));
}
/**
* Render the last modified date and time for the specified timestamp.
*
* @param lastModified Last modified date and time, in milliseconds since
* the epoch
*/
private String renderLastModified(long lastModified) {
return (timestamp.format(new Date(lastModified)));
}
/**
* Render the specified file size (in bytes).
*
* @param size File size (in bytes)
*/
private String renderSize(long size) {
long leftSide = size / 1024;
long rightSide = (size % 1024) / 103; // Makes 1 digit
if ((leftSide == 0) && (rightSide == 0) && (size > 0))
rightSide = 1;
return ("" + leftSide + "." + rightSide + " kb");
}
/**
* URL rewriter.
*
* @param path Path which has to be rewiten
*/
private String rewriteUrl(String path) {
String normalized = path;
// Replace " " with "%20"
while (true) {
int index = normalized.indexOf(" ");
if (index < 0)
break;
normalized = normalized.substring(0, index) + "%20"
+ normalized.substring(index + 1);
}
return normalized;
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/FileResources.java
Index: FileResources.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/FileResources.java,v 1.1 2000/08/11 22:45:57 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/08/11 22:45:57 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.resources;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.catalina.Context;
/**
* Implementation of the <b>Resources</b> that operates off a document
* base that is a directory in the local filesystem. If the specified
* document base is relative, it is resolved against the application base
* directory for our surrounding virtual host (if any), or against the
* value of the "catalina.home" system property.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that new files may
* be added, and existing files modified, while this web application is
* running. Therefore, only resources (not directories) are cached, and
* the background thread must remove cached entries that have been modified
* since they were cached.
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Revision: 1.1 $ $Date: 2000/08/11 22:45:57 $
*/
public final class FileResources extends ResourcesBase {
// ----------------------------------------------------- Instance Variables
/**
* The document base directory for this component.
*/
protected File base = null;
/**
* The descriptive information string for this implementation.
*/
protected static final String info =
"org.apache.catalina.resources.FileResources/1.0";
/**
* The descriptive information string for this implementation.
*/
protected static final int BUFFER_SIZE = 2048;
// ------------------------------------------------------------- Properties
/**
* Set the document root for this component.
*
* @param docBase The new document root
*
* @exception IllegalArgumentException if the specified value is not
* supported by this implementation
* @exception IllegalArgumentException if this would create a
* malformed URL
*/
public void setDocBase(String docBase) {
// Validate the format of the proposed document root
if (docBase == null)
throw new IllegalArgumentException
(sm.getString("resources.null"));
// Calculate a File object referencing this document base directory
File base = new File(docBase);
if (!base.isAbsolute())
base = new File(hostBase(), docBase);
// Validate that the document base is an existing directory
if (!base.exists() || !base.isDirectory() || !base.canRead())
throw new IllegalArgumentException
(sm.getString("fileResources.base", docBase));
this.base = base;
// Perform the standard superclass processing
super.setDocBase(docBase);
}
// --------------------------------------------------------- Public Methods
/**
* Return the real path for a given virtual path, or <code>null</code>
* if no such path can be identified.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public String getRealPath(String path) {
String normalized = normalize(path);
if (normalized == null)
return (null);
validate(normalized);
// Return a real path to where this file does, or would, exist
File file = new File(base, normalized.substring(1));
return (file.getAbsolutePath());
}
/**
* Return a URL to the resource specified by the given virtual path,
* or <code>null</code> if no such URL can be identified.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Use of this method bypasses any caching
* performed by this component. To take advantage of local caching,
* use <code>getResourceAsStream()</code> instead.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
* @exception MalformedURLException if the resulting URL does not
* have legal syntax
*/
public URL getResource(String path) throws MalformedURLException {
// Acquire an absolute pathname for the requested resource
String pathname = getRealPath(path);
if (pathname == null)
return (null);
// Construct a URL that refers to this file
return (new URL("file://" + pathname));
}
/**
* Return an InputStream to the contents of the resource specified
* by the given virtual path, or <code>null</code> if no resource
* exists at the specified path.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public InputStream getResourceAsStream(String path) {
String normalized = normalize(path);
if (normalized == null)
return (null);
validate(normalized);
// Look up the cached resource entry (if it exists) for this path
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null)
return (new ByteArrayInputStream(resource.getData()));
// Create a File object referencing the requested resource
File file = file(normalized);
if ((file == null) || !file.exists() || !file.canRead())
return (null);
// Special handling for directories
if (file.isDirectory()) {
String contextPath =
((Context) container).getPath();
if (contextPath == null)
contextPath = "";
DirectoryBean directory = new DirectoryBean(normalized, file);
File[] fileList = file.listFiles();
for (int i=0; i<fileList.length; i++) {
File currentFile = fileList[i];
ResourceBean newEntry = null;
if (currentFile.isDirectory()) {
newEntry =
new DirectoryBean(normalize(normalized + "/"
+ currentFile.getName()),
currentFile);
} else {
newEntry =
new ResourceBean(normalize(normalized + "/"
+ currentFile.getName()),
currentFile);
}
directory.addResource(newEntry);
}
return (directory.render(contextPath));
}
// Cache the data for this resource (if appropriate and not yet done)
if (cacheable(normalized, file.length())) {
resource = new ResourceBean(normalized, file);
try {
resource.cache(inputStream(resource.getName()));
} catch (IOException e) {
log(sm.getString("resources.input", resource.getName()), e);
return (null);
}
synchronized (resourcesCache) {
resourcesCache.put(resource.getName(), resource);
resourcesCount++;
}
return (new ByteArrayInputStream(resource.getData()));
}
// Serve the contents directly from the filesystem
try {
return (new FileInputStream(file));
} catch (IOException e) {
log(sm.getString("resoruces.input", resource.getName()), e);
return (null);
}
}
/**
* Returns true if a resource exists at the specified path,
* where <code>path</code> would be suitable for passing as an argument to
* <code>getResource()</code> or <code>getResourceAsStream()</code>.
* If there is no resource at the specified location, return false.
*
* @param path The path to the desired resource
*/
public boolean exists(String path) {
String normalized = normalize(path);
if (normalized == null)
return (false);
validate(normalized);
File file = new File(base, normalized.substring(1));
if (file != null)
return (file.exists());
else
return (false);
}
/**
* Return the last modified time for the resource specified by
* the given virtual path, or -1 if no such resource exists (or
* the last modified time cannot be determined).
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: We are assuming that
* files may be modified while the application is running, so we
* bypass the cache and check the filesystem directly.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public long getResourceModified(String path) {
String normalized = normalize(path);
if (normalized == null)
return (-1L);
validate(normalized);
String contextPath = ((Context) container).getPath();
File file = file(normalized);
if (file != null)
return (file.lastModified());
else
return (-1L);
}
/**
* Return the creation date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1. If this time is unknown, the
* implementation should return getResourceModified(path).
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: The creation date of a file
* shouldn't change except if the file is deleted and the recreated, so
* this method uses the cache.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public long getResourceCreated(String path) {
String normalized = normalize(path);
if (normalized == null)
return (-1L);
validate(normalized);
File file = file(normalized);
if (file != null)
return (file.lastModified());
else
return (-1L);
}
/**
* Return the content length of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If the content length
* of the resource can't be determined, return -1. If no content is
* available (when for exemple, the resource is a collection), return 0.
*
* @param path The path to the desired resource
*/
public long getResourceLength(String path) {
String normalized = normalize(path);
if (normalized == null)
return (-1L);
validate(normalized);
// Look up the cached resource entry (if it exists) for this path
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null) {
return (resource.getSize());
}
// No entry was found in the cache
File file = file(normalized);
if (file != null)
return (file.length());
else
return (-1L);
}
/**
* Return true if the resource at the specified path is a collection. A
* collection is a special type of resource which has no content but
* contains child resources.
*
* @param path The path to the desired resource
*/
public boolean isCollection(String path) {
String normalized = normalize(path);
if (normalized == null)
return (false);
validate(normalized);
File file = file(normalized);
if (file != null)
return (file.isDirectory());
else
return (false);
}
/**
* Return the children of the resource at the specified path, if any. This
* will return null if the resource is not a collection, or if it is a
* collection but has no children.
*
* @param path The path to the desired resource
*/
public String[] getCollectionMembers(String path) {
String normalized = normalize(path);
if (normalized == null)
return (null);
validate(normalized);
File file = file(normalized);
if (file != null) {
String[] dirList = file.list();
for (int i=0; i<dirList.length; i++) {
dirList[i] = normalize(normalized + "/" + dirList[i]);
}
return dirList;
} else {
return (null);
}
}
/**
* Set the content of the resource at the specified path. If the resource
* already exists, its previous content is overwritten. If the resource
* doesn't exist, its immediate parent collection (according to the path
* given) exists, then its created, and the given content is associated
* with it. Return false if either the resource is a collection, or
* no parent collection exist.
*
* @param path The path to the desired resource
* @param content InputStream to the content to be set
*/
public boolean setResource(String path, InputStream content) {
String normalized = normalize(path);
if (normalized == null)
return (false);
validate(normalized);
File file = new File(base, normalized.substring(1));
//if ((file.exists()) && (file.isDirectory()))
//return (false);
OutputStream os = null;
try {
os = new FileOutputStream(file);
} catch (FileNotFoundException e) {
return (false);
} catch (IOException e) {
return (false);
}
try {
byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
int nb = content.read(buffer);
if (nb == -1)
break;
os.write(buffer, 0, nb);
}
} catch (IOException e) {
return (false);
}
try {
os.close();
} catch (IOException e) {
return (false);
}
try {
content.close();
} catch (IOException e) {
return (false);
}
return (true);
}
/**
* Create a collection at the specified path. A parent collection for this
* collection must exist. Return false if a resource already exist at the
* path specified, or if the parent collection doesn't exist.
*
* @param path The path to the desired resource
*/
public boolean createCollection(String path) {
String normalized = normalize(path);
if (normalized == null)
return (false);
validate(normalized);
File file = new File(base, normalized.substring(1));
if (file != null)
return (file.mkdir());
else
return (false);
}
/**
* Delete the specified resource. Non-empty collections cannot be deleted
* before deleting all their member resources. Return false is deletion
* fails because either the resource specified doesn't exist, or the
* resource is a non-empty collection.
*
* @param path The path to the desired resource
*/
public boolean deleteResource(String path) {
String normalized = normalize(path);
if (normalized == null) {
return (false);
}
validate(normalized);
File file = file(normalized);
if (file != null) {
return (file.delete());
} else {
return (false);
}
}
// -------------------------------------------------------- Private Methods
/**
* Return a File object representing the specified normalized
* context-relative path if it exists and is readable. Otherwise,
* return <code>null</code>.
*
* @param name Normalized context-relative path (with leading '/')
*/
private File file(String name) {
if (name == null)
return (null);
File file = new File(base, name.substring(1));
if (file.exists() && file.canRead())
return (file);
else
return (null);
}
/**
* Return an input stream to the data content of the underlying file
* that corresponds to the specified normalized context-relative path.
*
* @param name Normalized context-relative path (with leading '/')
*
* @exception IOException if an input/output error occurs
*/
private InputStream inputStream(String name) throws IOException {
File file = file(name);
if ((file == null) || file.isDirectory())
return (null);
else
return (new FileInputStream(file));
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/JarResources.java
Index: JarResources.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/JarResources.java,v 1.1 2000/08/11 22:45:57 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/08/11 22:45:57 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.resources;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
/**
* Implementation of the <b>Resources</b> that decompresses and renders
* entries from a JAR file that is located either locally or remotely.
* Valid syntax for the <code>docBase</code> property corresponds to the
* syntax supported by the <code>java.net.JarURLConnection</code> class,
* and is illustrated by the following examples:
* <ul>
* <li><b>jar:file:/path/to/filename.jar!/</b> - Uses the JAR file
* <code>/path/to/filename.jar</code> as the source of resources.
* <li><b>jar:http://www.foo.com/bar/baz.jar!/</b> - Uses the JAR file
* retrieved by doing an HTTP GET operation on the URL
* <code>http://www.foo.com/bar/baz.jar</code> (which makes this
* instance of Catalina serve as a proxy for the specified application).
* </ul>
* In all cases, the <code>docBase</code> you specify must begin with
* <code>jar:</code>. If your <code>docBase</code> value does not end with
* "!/", this will be added for you.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that the underlying
* JAR file itself will not be modified without restarting this web application
* (or at least this <code>Resources</code> implementation). Therefore, the
* set of directory and resource entries is pre-loaded into our resource cache.
* The actual data associated with these resources is not cached until it is
* requested the first time (and passes the "cacheable" test).
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/08/11 22:45:57 $
*/
public final class JarResources extends ResourcesBase {
// ----------------------------------------------------- Instance Variables
/**
* The URLConnection to our JAR file.
*/
protected JarURLConnection conn = null;
/**
* The descriptive information string for this implementation.
*/
protected static final String info =
"org.apache.catalina.resources.JarResources/1.0";
/**
* The JarFile object associated with our document base.
*/
protected JarFile jarFile = null;
// ------------------------------------------------------------- Properties
/**
* Set the document root for this component.
*
* @param docBase The new document root
*
* @exception IllegalArgumentException if the specified value is not
* supported by this implementation
* @exception IllegalArgumentException if this would create a
* malformed URL
*/
public void setDocBase(String docBase) {
// Validate the format of the proposed document root
if (docBase == null)
throw new IllegalArgumentException
(sm.getString("resources.null"));
if (!docBase.startsWith("jar:"))
throw new IllegalArgumentException
(sm.getString("jarResources.syntax", docBase));
if (!docBase.endsWith("!/"))
docBase += "!/";
// Close any previous JAR that we have opened
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
log("Closing JAR file", e);
}
jarFile = null;
conn = null;
}
// Open a URLConnection to the specified JAR file
try {
URL url = new URL(docBase);
conn = (JarURLConnection) url.openConnection();
conn.setAllowUserInteraction(false);
conn.setDoInput(true);
conn.setDoOutput(false);
conn.connect();
jarFile = conn.getJarFile();
} catch (Exception e) {
log("Establishing connection", e);
throw new IllegalArgumentException
(sm.getString("resources.connect", docBase));
}
// Populate our cache of directory and resource entries
populate();
// Perform the standard superclass processing
super.setDocBase(docBase);
}
// --------------------------------------------------------- Public Methods
/**
* Return the real path for a given virtual path, or <code>null</code>
* if no such path can be identified.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public String getRealPath(String path) {
validate(path);
return (null); // JAR entries do not have a real path
}
/**
* Return a URL to the resource specified by the given virtual path,
* or <code>null</code> if no such URL can be identified.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Use of this method bypasses any caching
* performed by this component. To take advantage of local caching,
* use <code>getResourceAsStream()</code> instead.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
* @exception MalformedURLException if the resulting URL does not
* have legal syntax
*/
public URL getResource(String path) throws MalformedURLException {
validate(path);
// Construct a URL from the normalized version of the specified path
String normalized = normalize(path);
if (normalized != null)
return (new URL(docBase + normalized.substring(1)));
else
return (null);
}
/**
* Return an InputStream to the contents of the resource specified
* by the given virtual path, or <code>null</code> if no resource
* exists at the specified path.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public InputStream getResourceAsStream(String path) {
validate(path);
// Look up the cached resource entry (if it exists) for this path
String normalized = normalize(path);
if (normalized == null)
return (null);
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource == null)
return (null);
// Special handling for directories
if (resource instanceof DirectoryBean) {
if (expand) {
if (!(container instanceof Context))
return (null); // No way to find the context path
String contextPath =
((Context) container).getPath();
if (contextPath == null)
contextPath = "";
return (((DirectoryBean) resource).render(contextPath));
} else {
return (null);
}
}
// Cache the data for this resource (if appropriate and not yet done)
if (resource.getData() == null) {
try {
resource.cache(inputStream(resource.getName()));
} catch (IOException e) {
log(sm.getString("resources.input", resource.getName()), e);
return (null);
}
if (resource.getData() != null)
resourcesCount++;
}
// Return an input stream to the cached or uncached data
if (resource.getData() != null)
return (new ByteArrayInputStream(resource.getData()));
else {
try {
return (inputStream(normalized));
} catch (IOException e) {
log(sm.getString("resources.input", resource.getName()), e);
return (null);
}
}
}
/**
* Returns true if a resource exists at the specified path,
* where <code>path</code> would be suitable for passing as an argument to
* <code>getResource()</code> or <code>getResourceAsStream()</code>.
* If there is no resource at the specified location, return false.
*
* @param path The path to the desired resource
*/
public boolean exists(String path) {
// Look up and return the last modified time for this resource
String normalized = normalize(path);
if (normalized == null)
return (false);
validate(normalized);
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null)
return (true);
else
return (false);
}
/**
* Return the last modified time for the resource specified by
* the given virtual path, or -1 if no such resource exists (or
* the last modified time cannot be determined).
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: We are assuming that the
* underlying JAR file will not be modified without restarting our
* associated Context, so it is sufficient to return the last modified
* timestamp from our cached resource bean.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public long getResourceModified(String path) {
validate(path);
// Look up and return the last modified time for this resource
String normalized = normalize(path);
if (normalized == null)
return (-1L);
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null)
return (resource.getLastModified());
else
return (-1L);
}
/**
* Return the creation date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1. If this time is unknown, the
* implementation should return getResourceModified(path).
*
* @param path The path to the desired resource
*/
public long getResourceCreated(String path) {
return 0;
}
/**
* Return the content length of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If the content length
* of the resource can't be determined, return -1. If no content is
* available (when for exemple, the resource is a collection), return 0.
*
* @param path The path to the desired resource
*/
public long getResourceLength(String path) {
return -1;
}
/**
* Return true if the resource at the specified path is a collection. A
* collection is a special type of resource which has no content but
* contains child resources.
*
* @param path The path to the desired resource
*/
public boolean isCollection(String path) {
return false;
}
/**
* Return the children of the resource at the specified path, if any. This
* will return null if the resource is not a collection, or if it is a
* collection but has no children.
*
* @param path The path to the desired resource
*/
public String[] getCollectionMembers(String path) {
return null;
}
/**
* Set the content of the resource at the specified path. If the resource
* already exists, its previous content is overwritten. If the resource
* doesn't exist, its immediate parent collection (according to the path
* given) exists, then its created, and the given content is associated
* with it. Return false if either the resource is a collection, or
* no parent collection exist.
*
* @param path The path to the desired resource
* @param content InputStream to the content to be set
*/
public boolean setResource(String path, InputStream content) {
return false;
}
/**
* Create a collection at the specified path. A parent collection for this
* collection must exist. Return false if a resource already exist at the
* path specified, or if the parent collection doesn't exist.
*
* @param path The path to the desired resource
*/
public boolean createCollection(String path) {
return false;
}
/**
* Delete the specified resource. Non-empty collections cannot be deleted
* before deleting all their member resources. Return false is deletion
* fails because either the resource specified doesn't exist, or the
* resource is a non-empty collection.
*
* @param path The path to the desired resource
*/
public boolean deleteResource(String path) {
return false;
}
// -------------------------------------------------------- Private Methods
/**
* Return an input stream to the data content of the underlying JAR entry
* that corresponds to the specified normalized context-relative path.
*
* @param name Normalized context-relative path (with leading '/')
*
* @exception IOException if an input/output error occurs
*/
private InputStream inputStream(String name) throws IOException {
if (name == null)
return (null);
JarEntry entry = jarFile.getJarEntry(name.substring(1));
if (entry == null)
return (null);
return (jarFile.getInputStream(entry));
}
/**
* Populate our resources cache based on all of the entries in the
* underlying JAR file.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: This method assumes that the
* "name" of an entry within the JAR file is exactly the same as the
* result of performing a <code>normalize()</code> call on that name,
* with the exception of the leading slash that is added.
*/
private void populate() {
synchronized (resourcesCache) {
// Erase the existing cache
resourcesCache.clear();
resourcesCount = 0;
// Construct a pseudo-directory for the entire JAR file
DirectoryBean top = new DirectoryBean("/");
resourcesCache.put(top.getName(), top);
// Process the entries in this JAR file
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = (JarEntry) entries.nextElement();
// Create and cache the resource for this entry
String name = "/" + entry.getName();
ResourceBean resource = null;
if (entry.isDirectory())
resource = new DirectoryBean(name, entry);
else
resource = new ResourceBean(name, entry);
// Connect to our parent entry (if any)
int last = name.lastIndexOf("/");
String parentName = name.substring(0, last);
if (parentName.length() < 1)
parentName = "/";
ResourceBean parent =
(ResourceBean) resourcesCache.get(parentName);
if ((parent != null) && (parent instanceof DirectoryBean))
resource.setParent((DirectoryBean) parent);
}
}
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Shut down this component.
*
* @exception LifecycleException if a major problem occurs
*/
public void stop() throws LifecycleException {
// Shut down our current connection (if any)
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
log("Closing JAR file", e);
}
jarFile = null;
}
conn = null;
// Perform standard superclass processing
super.stop();
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/LocalStrings.properties
Index: LocalStrings.properties
===================================================================
directory.filename=Filename
directory.lastModified=Last Modified
directory.parent=Up To {0}
directory.size=Size
directory.title=Directory Listing For {0}
directory.version=Tomcat Catalina version 4.0
fileResources.base=Document base {0} does not exist or is not a readable directory
jarResources.syntax=Document base {0} must start with 'jar:' and end with '!/'
resources.alreadyStarted=Resources has already been started
resources.connect=Cannot connect to document base {0}
resources.input=Cannot create input stream for resource {0}
resources.notStarted=Resources has not yet been started
resources.null=Document base cannot be null
resources.path=Context relative path {0} must start with '/'
standardResources.alreadyStarted=Resources has already been started
standardResources.directory=File base {0} is not a directory
standardResources.exists=File base {0} does not exist
standardResources.notStarted=Resources has not yet been started
standardResources.null=Document base cannot be null
standardResources.slash=Document base {0} must not end with a slash
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/ResourceBean.java
Index: ResourceBean.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/ResourceBean.java,v 1.1 2000/08/11 22:45:58 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/08/11 22:45:58 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.resources;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.jar.JarEntry;
/**
* Abstraction bean that represents the properties of a "resource" that
* may or may not be a "directory", in a fashion that is independent
* of the actual underlying medium used to represent those entries.
* Convenient constructors are provided to populate our properties from
* common sources, but it is feasible to do everything with property
* setters if necessary.
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/08/11 22:45:58 $
*/
public class ResourceBean {
// ----------------------------------------------------------- Constructors
/**
* Construct a new resource bean for the named resource, with default
* properties.
*
* @param name Normalized context-relative name of this resource
*/
public ResourceBean(String name) {
super();
setName(name);
}
/**
* Construct a new resource bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param file File representing this resource entry
*/
public ResourceBean(String name, File file) {
this(name);
populate(file);
}
/**
* Construct a new resource bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param entry JAR entry representing this resource entry
*/
public ResourceBean(String name, JarEntry entry) {
this(name);
populate(entry);
}
// ------------------------------------------------------------- Properties
/**
* The data content of this resource. This property is
* <strong>only</strong> initialized when the corresponding property
* setter method is called.
*/
protected byte[] data = null;
public byte[] getData() {
return (this.data);
}
public void setData(byte[] data) {
this.data = data;
}
/**
* The last modified date/time for this resource, in milliseconds since
* the epoch.
*/
protected long lastModified = 0L;
public long getLastModified() {
return (this.lastModified);
}
public void setLastModified(long lastModified) {
this.lastModified = lastModified;
}
/**
* The normalized context-relative name of this resource.
*/
protected String name = null;
public String getName() {
return (this.name);
}
public void setName(String name) {
this.name = name;
}
/**
* The parent resource (normally a directory entry) of this resource.
* Note that this property is <strong>not</strong> set from an underlying
* File or JarEntry argument to our constructor -- you must call
* <code>setParent()</code> explicitly if you wish to maintain this
* relationship.
*/
protected DirectoryBean parent = null;
public DirectoryBean getParent() {
return (this.parent);
}
public void setParent(DirectoryBean parent) {
if (this.parent != null)
this.parent.removeResource(this);
this.parent = parent;
if (this.parent != null)
this.parent.addResource(this);
}
/**
* The size of this resource, in bytes.
*/
protected long size = 0L;
public long getSize() {
return (this.size);
}
public void setSize(long size) {
this.size = size;
}
// --------------------------------------------------------- Public Methods
/**
* Cache the data for this resource from the specified input stream.
*
* @param input InputStream from which to read the data for this resource
*
* @exception IOException if an input/output error occurs
*/
public void cache(InputStream input) throws IOException {
BufferedInputStream in = new BufferedInputStream(input);
ByteArrayOutputStream out = new ByteArrayOutputStream();
while (true) {
int ch = in.read();
if (ch < 0)
break;
out.write(ch);
}
in.close();
data = out.toByteArray();
}
// ------------------------------------------------------ Protected Methods
/**
* Populate our properties from the specified File object.
*
* @param file File representing this entry
*/
protected void populate(File file) {
this.lastModified = file.lastModified();
this.size = file.length();
}
/**
* Populate our properties from the specified JarEntry object.
*
* @param entry JarEntry representing this entry
*/
protected void populate(JarEntry entry) {
this.lastModified = entry.getTime();
this.size = entry.getSize();
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/ResourcesBase.java
Index: ResourcesBase.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/ResourcesBase.java,v 1.1 2000/08/11 22:45:58 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/08/11 22:45:58 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.resources;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Logger;
import org.apache.catalina.Resources;
import org.apache.catalina.util.LifecycleSupport;
import org.apache.catalina.util.StringManager;
/**
* Convenience base class for implementations of the <b>Resources</b>
* interface. It is expected that subclasses of this class will be
* created for each flavor of document root to be supported.
* <p>
* Included in the basic support provided by this class is provisions
* for caching of resources according to configurable policy properties.
* This will be especially useful for web applications with relatively
* small amounts of static content (such as a 100% dynamic JSP based
* application with just a few images), as well as environments where
* accessing the underlying resources is relatively time consuming
* (such as a local or remote JAR file).
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/08/11 22:45:58 $
*/
public abstract class ResourcesBase
implements Resources, Lifecycle, PropertyChangeListener, Runnable {
// ----------------------------------------------------------- Constructors
/**
* Construct a new instance of this class with default values.
*/
public ResourcesBase() {
super();
}
// ----------------------------------------------------- Instance Variables
/**
* The interval (in seconds) at which our background task should check
* for out-of-date cached resources, or zero for no checks.
*/
protected int checkInterval = 0;
/**
* The Container this component is associated with (normally a Context).
*/
protected Container container = null;
/**
* The debugging detail level for this component.
*/
protected int debug = 0;
/**
* The document root for this component.
*/
protected String docBase = null;
/**
* Should "directory" entries be expanded?
*/
protected boolean expand = true;
/**
* The descriptive information string for this implementation.
*/
protected static final String info =
"org.apache.catalina.resources.ResourcesBase/1.0";
/**
* The lifecycle event support for this component.
*/
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
/**
* The maximum number of resources to cache.
*/
protected int maxCount = 0;
/**
* The maximum size of resources to be cached.
*/
protected long maxSize = 0L;
/**
* The minimum size of resources to be cached.
*/
protected long minSize = 0L;
/**
* The prefix to the log messages we will be creating.
*/
protected static final String prefix = "ResourcesBase";
/**
* The set of ResourceBean entries for this component,
* keyed by the normalized context-relative resource URL.
*/
protected HashMap resourcesCache = new HashMap();
/**
* The count of ResourceBean entries for which we have actually
* cached data. This can be different from the number of elements
* in the <code>resourcesCache</code> collection.
*/
protected int resourcesCount = 0;
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* Has this component been started?
*/
protected boolean started = false;
/**
* The property change support for this component.
*/
protected PropertyChangeSupport support = new PropertyChangeSupport(this);
/**
* The background thread.
*/
protected Thread thread = null;
/**
* The background thread completion semaphore.
*/
protected boolean threadDone = false;
/**
* The name to register for the background thread.
*/
protected String threadName = "ResourcesBase";
// ------------------------------------------------------------- Properties
/**
* Return the resource cache check interval.
*/
public int getCheckInterval() {
return (this.checkInterval);
}
/**
* Set the resource cache check interval.
*
* @param checkInterval The new check interval
*/
public void setCheckInterval(int checkInterval) {
// Perform the property update
int oldCheckInterval = this.checkInterval;
this.checkInterval = checkInterval;
support.firePropertyChange("checkInterval",
new Integer(oldCheckInterval),
new Integer(this.checkInterval));
// Start or stop the background thread (if necessary)
if (started) {
if ((oldCheckInterval > 0) && (this.checkInterval <= 0))
threadStop();
else if ((oldCheckInterval <= 0) && (this.checkInterval > 0))
threadStart();
}
}
/**
* Return the Container with which this Resources has been associated.
*/
public Container getContainer() {
return (this.container);
}
/**
* Set the Container with which this Resources has been associated.
*
* @param container The associated Container
*/
public void setContainer(Container container) {
Container oldContainer = this.container;
if ((oldContainer != null) && (oldContainer instanceof Context))
((Context) oldContainer).removePropertyChangeListener(this);
this.container = container;
// We are interested in property changes to the document base
if ((this.container != null) && (this.container instanceof Context)) {
((Context) this.container).addPropertyChangeListener(this);
setDocBase(((Context) this.container).getDocBase());
}
support.firePropertyChange("container", oldContainer, this.container);
}
/**
* Return the debugging detail level for this component.
*/
public int getDebug() {
return (this.debug);
}
/**
* Set the debugging detail level for this component.
*
* @param debug The new debugging detail level
*/
public void setDebug(int debug) {
int oldDebug = this.debug;
this.debug = debug;
support.firePropertyChange("debug", new Integer(oldDebug),
new Integer(this.debug));
}
/**
* Return the document root for this component.
*/
public String getDocBase() {
return (this.docBase);
}
/**
* Set the document root for this component.
*
* @param docBase The new document root
*
* @exception IllegalArgumentException if the specified value is not
* supported by this implementation
* @exception IllegalArgumentException if this would create a
* malformed URL
*/
public void setDocBase(String docBase) {
// Validate the format of the proposed document root
if (docBase == null)
throw new IllegalArgumentException
(sm.getString("resources.null"));
// Change the document root property
String oldDocBase = this.docBase;
this.docBase = docBase.toString();
support.firePropertyChange("docBase", oldDocBase, this.docBase);
if (debug >= 1)
log("Setting docBase to '" + this.docBase + "'");
}
/**
* Return the "expand directories" flag.
*/
public boolean getExpand() {
return (this.expand);
}
/**
* Set the "expand directories" flag.
*
* @param expand The new "expand directories" flag
*/
public void setExpand(boolean expand) {
boolean oldExpand = this.expand;
this.expand = expand;
support.firePropertyChange("expand", new Boolean(oldExpand),
new Boolean(this.expand));
}
/**
* Return descriptive information about this Resources implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo() {
return (info);
}
/**
* Return the maximum number of resources to cache.
*/
public int getMaxCount() {
return (this.maxCount);
}
/**
* Set the maximum number of resources to cache.
*
* @param maxCount The new maximum count
*/
public void setMaxCount(int maxCount) {
int oldMaxCount = this.maxCount;
this.maxCount = maxCount;
support.firePropertyChange("maxCount", new Integer(oldMaxCount),
new Integer(this.maxCount));
}
/**
* Return the maximum size of resources to be cached.
*/
public long getMaxSize() {
return (this.maxSize);
}
/**
* Set the maximum size of resources to be cached.
*
* @param maxSize The new maximum size
*/
public void setMaxSize(long maxSize) {
long oldMaxSize = this.maxSize;
this.maxSize = maxSize;
support.firePropertyChange("maxSize", new Long(oldMaxSize),
new Long(this.maxSize));
}
/**
* Return the minimum size of resources to be cached.
*/
public long getMinSize() {
return (this.minSize);
}
/**
* Set the minimum size of resources to be cached.
*
* @param minSize The new minimum size
*/
public void setMinSize(long minSize) {
long oldMinSize = this.minSize;
this.minSize = minSize;
support.firePropertyChange("minSize", new Long(oldMinSize),
new Long(this.minSize));
}
// --------------------------------------------------------- Public Methods
/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/**
* Return the MIME type of the specified file, or <code>null</code> if
* the MIME type is not known. The MIME type is determined by the
* configuration of the servlet container, and may be specified in a
* web application descriptor. Common MIME types are
* <code>"text/html"</code> and <code>"image/gif"</code>.
* <p>
* The default implementation consults the MIME type mappings that have
* been registered in our associated Context, if any.
*
* @param file Name of the file whose MIME type is to be determined
*/
public String getMimeType(String file) {
if (debug >= 1)
log("Calculating MIME type of '" + file + "'");
if (file == null)
return (null);
if ((container == null) || (!(container instanceof Context)))
return (null);
int period = file.lastIndexOf(".");
if (period < 0)
return (null);
String extension = file.substring(period + 1);
if (extension.length() < 1)
return (null);
if (debug >= 1)
log(" Mime type of '" + extension + "' is '" +
((Context) container).findMimeMapping(extension));
return (((Context) container).findMimeMapping(extension));
}
/**
* Return the real path for a given virtual path. For example, the
* virtual path <code>"/index.html"</code> has a real path of whatever
* file on the server's filesystem would be served by a request for
* <code>"/index.html"</code>.
* <p>
* The real path returned will be in a form appropriate to the computer
* and operating system on which the servlet container is running,
* including the proper path separators. This method returns
* <code>null</code> if the servlet container cannot translate the
* virtual path to a real path for any reason (such as when the content
* is being made available from a <code>.war</code> archive).
*
* @param path The virtual path to be translated
*/
public abstract String getRealPath(String path);
/**
* Return a URL to the resource that is mapped to the specified path.
* The path must begin with a "/" and is interpreted as relative to
* the current context root.
* <p>
* This method allows the Container to make a resource available to
* servlets from any source. Resources can be located on a local or
* remote file system, in a database, or in a <code>.war</code> file.
* <p>
* The servlet container must implement the URL handlers and
* <code>URLConnection</code> objects that are necessary to access
* the resource.
* <p>
* This method returns <code>null</code> if no resource is mapped to
* the pathname.
* <p>
* Some Containers may allow writing to the URL returned by this method,
* using the methods of the URL class.
* <p>
* The resource content is returned directly, so be aware that
* requesting a <code>.jsp</code> page returns the JSP source code.
* Use a <code>RequestDispatcher</code> instead to include results
* of an execution.
* <p>
* This method has a different purpose than
* <code>java.lang.Class.getResource()</code>, which looks up resources
* based on a class loader. This method does not use class loaders.
*
* @param path The path to the desired resource
*
* @exception MalformedURLException if the pathname is not given
* in the correct form
*/
public abstract URL getResource(String path) throws MalformedURLException;
/**
* Return the resource located at the named path as an
* <code>InputStream</code> object.
* <p>
* The data in the <code>InputStream</code> can be of any type or length.
* The path must be specified according to the rules given in
* <code>getResource()</code>. This method returns <code>null</code>
* if no resource exists at the specified path.
* <p>
* Meta-information such as content length and content type that is
* available via the <code>getResource()</code> method is lost when
* using this method.
* <p>
* The servlet container must implement the URL handlers and
* <code>URLConnection</code> objects that are necessary to access
* the resource.
* <p>
* This method is different from
* <code>java.lang.Class.getResourceAsStream()</code>, which uses a
* class loader. This method allows servlet containers to make a
* resource available to a servlet from any location, without using
* a class loader.
*
* @param path The path to the desired resource
*/
public abstract InputStream getResourceAsStream(String path);
/**
* Returns true if a resource exists at the specified path,
* where <code>path</code> would be suitable for passing as an argument to
* <code>getResource()</code> or <code>getResourceAsStream()</code>.
* If there is no resource at the specified location, return false.
*
* @param path The path to the desired resource
*/
public abstract boolean exists(String path);
/**
* Return the last modified date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1.
* <p>
* <b>IMPLEMENTATION NOTE</b>: This method should bypass any cached
* resources and reference the underlying resource directly, because it
* will be used by the background thread that is checking for resources
* that have been modified.
*
* @param path The path to the desired resource
*/
public abstract long getResourceModified(String path);
/**
* Return the creation date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1. If this time is unknown, the
* implementation should return getResourceModified(path).
*
* @param path The path to the desired resource
*/
public abstract long getResourceCreated(String path);
/**
* Return the content length of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If the content length
* of the resource can't be determined, return -1. If no content is
* available (when for exemple, the resource is a collection), return 0.
*
* @param path The path to the desired resource
*/
public abstract long getResourceLength(String path);
/**
* Return true if the resource at the specified path is a collection. A
* collection is a special type of resource which has no content but
* contains child resources.
*
* @param path The path to the desired resource
*/
public abstract boolean isCollection(String path);
/**
* Return the children of the resource at the specified path, if any. This
* will return null if the resource is not a collection, or if it is a
* collection but has no children.
*
* @param path The path to the desired resource
*/
public abstract String[] getCollectionMembers(String path);
/**
* Set the content of the resource at the specified path. If the resource
* already exists, its previous content is overwritten. If the resource
* doesn't exist, its immediate parent collection (according to the path
* given) exists, then its created, and the given content is associated
* with it. Return false if either the resource is a collection, or
* no parent collection exist.
*
* @param path The path to the desired resource
* @param content InputStream to the content to be set
*/
public abstract boolean setResource(String path, InputStream content);
/**
* Create a collection at the specified path. A parent collection for this
* collection must exist. Return false if a resource already exist at the
* path specified, or if the parent collection doesn't exist.
*
* @param path The path to the desired resource
*/
public abstract boolean createCollection(String path);
/**
* Delete the specified resource. Non-empty collections cannot be deleted
* before deleting all their member resources. Return false is deletion
* fails because either the resource specified doesn't exist, or the
* resource is a non-empty collection.
*
* @param path The path to the desired resource
*/
public abstract boolean deleteResource(String path);
/**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
// ----------------------------------------- PropertyChangeListener Methods
/**
* Process property change events from our associated Context.
*
* @param event The property change event that has occurred
*/
public void propertyChange(PropertyChangeEvent event) {
// Validate the source of this event
if (!(event.getSource() instanceof Context))
return;
Context context = (Context) event.getSource();
// Process a relevant property change
if (event.getPropertyName().equals("docBase"))
setDocBase((String) event.getNewValue());
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Add a lifecycle event listener to this component.
*
* @param listener The listener to add
*/
public void addLifecycleListener(LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
}
/**
* Remove a lifecycle event listener from this component.
*
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener) {
lifecycle.removeLifecycleListener(listener);
}
/**
* Prepare for the beginning of active use of the public methods of this
* component. This method should be called after <code>configure()</code>,
* and before any of the public methods of the component are utilized.
*
* @exception IllegalStateException if this component has already been
* started
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() throws LifecycleException {
// Validate and update our current component state
if (started)
throw new LifecycleException
(sm.getString("resources.alreadyStarted"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start the background expiration checking thread (if necessary)
if (checkInterval > 0)
threadStart();
}
/**
* Gracefully terminate the active use of the public methods of this
* component. This method should be the last one called on a given
* instance of this component.
*
* @exception IllegalStateException if this component has not been started
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() throws LifecycleException {
// Validate and update our current state
if (!started)
throw new LifecycleException
(sm.getString("resources.notStarted"));
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
// Stop the background expiration checking thread (if necessary)
threadStop();
}
// ------------------------------------------------------ Protected Methods
/**
* Should the resource specified by our parameters be cached?
*
* @param name Name of the proposed resource
* @param size Size (in bytes) of the proposed resource
*/
protected boolean cacheable(String name, long size) {
if ((size < minSize) || (size > maxSize))
return (false);
else if (resourcesCount >= maxCount)
return (false);
else
return (true);
}
/**
* Return a File object representing the base directory for the
* entire servlet container (i.e. the Engine container if present).
*/
protected File engineBase() {
return (new File(System.getProperty("catalina.home")));
}
/**
* Return a File object representing the base directory for the
* current virtual host (i.e. the Host container if present).
*/
protected File hostBase() {
// Locate our immediately surrounding Host (if any)
Container container = this.container;
while (container != null) {
if (container instanceof Host)
break;
container = container.getParent();
}
if (container == null)
return (engineBase());
// Use the "appBase" property of this container
String appBase = ((Host) container).getAppBase();
File file = new File(appBase);
if (!file.isAbsolute())
file = new File(engineBase(), appBase);
return (file);
}
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
*/
protected void log(String message) {
Logger logger = null;
if (container != null)
logger = container.getLogger();
if (logger != null)
logger.log(prefix + "[" + container.getName() + "]: "
+ message);
else {
String containerName = null;
if (container != null)
containerName = container.getName();
System.out.println(prefix + "[" + containerName
+ "]: " + message);
}
}
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
* @param throwable Associated exception
*/
protected void log(String message, Throwable throwable) {
Logger logger = null;
if (container != null)
logger = container.getLogger();
if (logger != null)
logger.log(prefix + "[" + container.getName() + "] "
+ message, throwable);
else {
String containerName = null;
if (container != null)
containerName = container.getName();
System.out.println(prefix + "[" + containerName
+ "]: " + message);
System.out.println("" + throwable);
throwable.printStackTrace(System.out);
}
}
/**
* Return a context-relative path, beginning with a "/", that represents
* the canonical version of the specified path after ".." and "." elements
* are resolved out. If the specified path attempts to go outside the
* boundaries of the current context (i.e. too many ".." path elements
* are present), return <code>null</code> instead.
*
* @param path Path to be normalized
*/
protected String normalize(String path) {
// Normalize the slashes and add leading slash if necessary
String normalized = path;
if (normalized.indexOf('\\') >= 0)
normalized = normalized.replace('\\', '/');
if (!normalized.startsWith("/"))
normalized = "/" + normalized;
// Resolve occurrences of "//" in the normalized path
while (true) {
int index = normalized.indexOf("//");
if (index < 0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 1);
}
// Resolve occurrences of "%20" in the normalized path
while (true) {
int index = normalized.indexOf("%20");
if (index < 0)
break;
normalized = normalized.substring(0, index) + " " +
normalized.substring(index + 3);
}
// Resolve occurrences of "/./" in the normalized path
while (true) {
int index = normalized.indexOf("/./");
if (index < 0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 2);
}
// Resolve occurrences of "/../" in the normalized path
while (true) {
int index = normalized.indexOf("/../");
if (index < 0)
break;
if (index == 0)
return (null); // Trying to go outside our context
int index2 = normalized.lastIndexOf('/', index - 1);
normalized = normalized.substring(0, index2) +
normalized.substring(index + 3);
}
// Return the normalized path that we have completed
return (normalized);
}
/**
* Scan our cached resources, looking for cases where the underlying
* resource has been modified since we cached it.
*/
protected void threadProcess() {
// Create a list of the cached resources we know about
ResourceBean entries[] = new ResourceBean[0];
synchronized(resourcesCache) {
entries =
(ResourceBean[]) resourcesCache.values().toArray(entries);
}
// Check the last modified date on each entry
for (int i = 0; i < entries.length; i++) {
if (entries[i].getLastModified() !=
getResourceModified(entries[i].getName())) {
synchronized (resourcesCache) {
resourcesCache.remove(entries[i].getName());
}
}
}
}
/**
* Sleep for the duration specified by the <code>checkInterval</code>
* property.
*/
protected void threadSleep() {
try {
Thread.sleep(checkInterval * 1000L);
} catch (InterruptedException e) {
;
}
}
/**
* Start the background thread that will periodically check for
* session timeouts.
*/
protected void threadStart() {
if (thread != null)
return;
threadDone = false;
threadName = "ResourcesBase[" + container.getName() + "]";
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
}
/**
* Stop the background thread that is periodically checking for
* session timeouts.
*/
protected void threadStop() {
if (thread == null)
return;
threadDone = true;
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
;
}
thread = null;
}
/**
* Validate the format of the specified path, which should be context
* relative and begin with a slash character.
*
* @param path Context-relative path to be validated
*
* @exception IllegalArgumentException if the specified path is null
* or does not have a valid format
*/
protected void validate(String path) {
if ((path == null) || !path.startsWith("/"))
throw new IllegalArgumentException
(sm.getString("resources.path", path));
}
// ------------------------------------------------------ Background Thread
/**
* The background thread that checks for session timeouts and shutdown.
*/
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
threadProcess();
}
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/resources/package.html
Index: package.html
===================================================================
<body>
<p>This package contains <code>Resources</code> implementations for the
various supported repositories for static resource files. They may be used
(by a <code>Context</code> implementation) in implementing the following
API calls:</p>
<ul>
<li><b>public URL getResource(String path)</b></li>
<li><b>publid InputStream getResourceAsStream(String path)</b></li>
</ul>
<p>The implementations share a common base class that supports caching of
resources where appropriate. Caching (and other features) of all the
standard <code>Resources</code> implementations are configured by setting
the following properties (default values are in square brackets):</p>
<ul>
<li><b>checkInterval</b> - The interval, in seconds, between checks by our
background thread for out-of-date cached resources, or zero for no
checks. [0]</li>
<li><b>debug</b> - Debugging detail level that controls our logging, where
zero means no debugging messages and higher numbers mean more deatail.
[0]</li>
<li><b>docBase</b> - The document base for this <code>Resources</code>
implementation, which must follow the syntax specified for each
individual implementation. This property is transparently duplicated
from the <code>docBase</code> property of the <code>Context</code>
we are associated with. [REQUIRED - NO DEFAULT]
<li><b>expand</b> - Should getResource() calls for a "directory" return a
directory listing (if "true") or nothing (if "false")?
[true]</li>
<li><b>maxCount</b> - The maximum number of resources to cache, or zero
for no limit. [0]
<li><b>maxSize</b> - The maximum size (in bytes) of resources to cache, or
zero for no limit. [0]
<li><b>minSize</b> - The minimum size (in bytes) of resources to cache, or
zero for no limit. [0]
</ul>
<p>The standard <code>Resources</code> implementations that are currently
available include the following (with additional configuration properties
as specified):</p>
<ul>
<li><b>FileResources</b> - Implementation of <code>Resources</code> that
operates off a document base that is a directory in the server's
local filesystem. The document base must specify a relative or absolute
pathname. If it is relative, it is resolved against the application
base for our surrounding <code>Host</code> (if any), or against the
value of the "catalina.home" system property. Files can be added, removed,
or modified within this directory while the application runs -- a
background thread periodically checks the last modified time of all
cached resources and un-caches entries that have been modified, so that
the updated version will be processed the next time it is requested.</li>
<li><b>JarResources</b> - Implementation of <code>Resources</code> that
operates off a web application archive (WAR) file directly. In this
environment, there is no concept of "file paths" to the various entries,
so <code>getRealPath()</code> will always return <code>null</code>. In
addition, this implementation assumes that the individual entries in the
WAR file, or the WAR file itself, will not be modified while the
application is running -- therefore, no checking for out-of-date cache
entries is required. Caching is still performed, subject to the policies
enforced by the <code>cacheable()</code> method, because the JAR file
itself might be on a remote server.</li>
</ul>
</body>