From 707df15661633e3c6a2f784918eac89058a5c33b Mon Sep 17 00:00:00 2001 From: markt Date: Thu, 22 Apr 2010 08:13:08 +0000 Subject: [PATCH] Add support for loading static resources from /META-INF/resources inside JARs bundled with a web application git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@936646 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/Context.java | 6 + .../apache/catalina/core/LocalStrings.properties | 1 + java/org/apache/catalina/core/StandardContext.java | 17 +++ .../org/apache/catalina/startup/ContextConfig.java | 37 ++++++ .../catalina/startup/LocalStrings.properties | 1 + .../apache/naming/resources/BaseDirContext.java | 128 ++++++++++++++++++++- .../apache/naming/resources/FileDirContext.java | 11 +- .../org/apache/naming/resources/WARDirContext.java | 16 +-- 8 files changed, 198 insertions(+), 19 deletions(-) diff --git a/java/org/apache/catalina/Context.java b/java/org/apache/catalina/Context.java index 9e5c46770..ef2bd3cfc 100644 --- a/java/org/apache/catalina/Context.java +++ b/java/org/apache/catalina/Context.java @@ -1183,5 +1183,11 @@ public interface Context extends Container { */ public JspConfigDescriptor getJspConfigDescriptor(); + /** + * Add a URL for a JAR that contains static resources in a + * META-INF/resources directory that should be included in the static + * resources for this context. + */ + public void addResourceJarUrl(URL url); } diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties index ddabbdff4..2a774697b 100644 --- a/java/org/apache/catalina/core/LocalStrings.properties +++ b/java/org/apache/catalina/core/LocalStrings.properties @@ -115,6 +115,7 @@ standardContext.loginConfig.loginPage=Form login page {0} must start with a ''/' standardContext.loginConfig.loginWarning=WARNING: Form login page {0} must start with a ''/'' in Servlet 2.4 standardContext.loginConfig.required=LoginConfig cannot be null standardContext.mappingError=MAPPING configuration error for relative URI {0} +standardContext.noResourceJar=Resource JARs are not supported. The JAR found at [{0}] will no be used to provide static content for context with path [{1}] standardContext.notFound=The requested resource ({0}) is not available. standardContext.notReloadable=Reloading is disabled on this Context standardContext.notStarted=Context has not yet been started diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java index eedc91c1f..4c1c58bf4 100644 --- a/java/org/apache/catalina/core/StandardContext.java +++ b/java/org/apache/catalina/core/StandardContext.java @@ -962,6 +962,21 @@ public class StandardContext /** + * Add a URL for a JAR that contains static resources in a + * META-INF/resources directory that should be included in the static + * resources for this context. + */ + public void addResourceJarUrl(URL url) { + if (webappResources instanceof BaseDirContext) { + ((BaseDirContext) webappResources).addResourcesJar(url); + } else { + log.error(sm.getString("standardContext.noResourceJar", url, + getPath())); + } + } + + + /** * Set the current alias configuration. The list of aliases should be of the * form "/aliasPath1=docBase1,/aliasPath2=docBase2" where aliasPathN must * include a leading '/' and docBaseN must be an absolute path to either a @@ -2122,11 +2137,13 @@ public class StandardContext return; if (resources instanceof BaseDirContext) { + // Caching ((BaseDirContext) resources).setCached(isCachingAllowed()); ((BaseDirContext) resources).setCacheTTL(getCacheTTL()); ((BaseDirContext) resources).setCacheMaxSize(getCacheMaxSize()); ((BaseDirContext) resources).setCacheObjectMaxSize( getCacheObjectMaxSize()); + // Alias support ((BaseDirContext) resources).setAliases(getAliases()); } if (resources instanceof FileDirContext) { diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java index b248c6832..81c95aef7 100644 --- a/java/org/apache/catalina/startup/ContextConfig.java +++ b/java/org/apache/catalina/startup/ContextConfig.java @@ -38,6 +38,7 @@ import java.util.Properties; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.zip.ZipEntry; import javax.servlet.ServletContext; @@ -1237,6 +1238,8 @@ public class ContextConfig if (context.getLogEffectiveWebXml()) { log.info("web.xml:\n" + mergedWebXml); } + + processResourceJARs(orderedFragments); } else { // Apply unmerged web.xml to Context webXml.configureContext(context); @@ -1245,6 +1248,40 @@ public class ContextConfig /** + * Scan JARs that contain web-fragment.xml files that will be used to + * configure this application to see if they also contain static resources. + * If static resources are found, add them to the context. Resources are + * added in web-fragment.xml priority order. + */ + protected void processResourceJARs(Set fragments) { + for (WebXml fragment : fragments) { + URL jarUrl = fragment.getURL(); + JarFile jarFile = null; + try { + JarURLConnection conn = + (JarURLConnection) jarUrl.openConnection(); + jarFile = conn.getJarFile(); + ZipEntry entry = jarFile.getEntry("META-INF/resources/"); + if (entry != null) { + context.addResourceJarUrl(jarUrl); + } + } catch (IOException ioe) { + log.error(sm.getString("contextConfig.resourceJarFail", jarUrl, + context.getPath())); + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException e) { + // Ignore + } + } + } + } + } + + + /** * Identify the default web.xml to be used and obtain an input source for * it. */ diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties index 6f3fe5d47..5644b079f 100644 --- a/java/org/apache/catalina/startup/LocalStrings.properties +++ b/java/org/apache/catalina/startup/LocalStrings.properties @@ -46,6 +46,7 @@ contextConfig.jar=Unable to process resource [{0}] for annotations contextConfig.jndiUrl=Unable to process JNDI URL [{0}] for annotations contextConfig.jndiUrlNotDirContextConn=The connection created for URL [{0}] was not a DirContextURLConnection contextConfig.missingRealm=No Realm has been configured to authenticate against +contextConfig.resourceJarFail=Failed to processes JAR found at URL [{0}] for static resources to be included in context with path [{0}] contextConfig.role.auth=WARNING: Security role name {0} used in an without being defined in a contextConfig.role.link=WARNING: Security role name {0} used in a without being defined in a contextConfig.role.runas=WARNING: Security role name {0} used in a without being defined in a diff --git a/java/org/apache/naming/resources/BaseDirContext.java b/java/org/apache/naming/resources/BaseDirContext.java index f8b34e5ad..89e50b3c5 100644 --- a/java/org/apache/naming/resources/BaseDirContext.java +++ b/java/org/apache/naming/resources/BaseDirContext.java @@ -19,11 +19,18 @@ package org.apache.naming.resources; import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; import javax.naming.Binding; import javax.naming.Context; @@ -131,10 +138,39 @@ public abstract class BaseDirContext implements DirContext { new HashMap(); + /** + * Alternate / backup DirContexts for static resources. These will be + * searched in the order they are added if the requested resource cannot be + * found in the primary DirContext. + */ + protected List altDirContexts = new ArrayList(); + + // ------------------------------------------------------------- Properties /** + * Add a resources JAR. The contents of /META-INF/resources/ will be used if + * a requested resource can not be found in the main context. + */ + public void addResourcesJar(URL url) { + try { + JarURLConnection conn = (JarURLConnection) url.openConnection(); + JarFile jarFile = conn.getJarFile(); + ZipEntry entry = jarFile.getEntry("/"); + WARDirContext warDirContext = new WARDirContext(jarFile, + new WARDirContext.Entry("/", entry)); + warDirContext.loadEntries(); + altDirContexts.add(warDirContext); + } catch (IOException ioe) { + // TODO: Log failure + } finally { + // TODO: Clean up + } + } + + + /** * Add an alias. */ public void addAlias(String path, BaseDirContext dirContext) { @@ -350,7 +386,25 @@ public abstract class BaseDirContext implements DirContext { return result.dirContext.doGetRealPath(result.aliasName); } } - return doGetRealPath(name); + + // Next do a standard getRealPath() + String path = doGetRealPath(name); + + if (path != null) + return path; + + // Check the alternate locations + for (DirContext altDirContext : altDirContexts) { + if (altDirContext instanceof BaseDirContext){ + path = ((BaseDirContext) altDirContext).getRealPath( + "META-INF/resources/" + name); + if (path != null) + return path; + } + } + + // Really not found + return null; } // -------------------------------------------------------- Context Methods @@ -380,13 +434,29 @@ public abstract class BaseDirContext implements DirContext { * @exception NamingException if a naming exception is encountered */ public final Object lookup(String name) throws NamingException { + // First check for aliases if (!aliases.isEmpty()) { AliasResult result = findAlias(name); if (result.dirContext != null) { return result.dirContext.lookup(result.aliasName); } } - return doLookup(name); + + // Next do a standard lookup + Object obj = doLookup(name); + + if (obj != null) + return obj; + + // Check the alternate locations + for (DirContext altDirContext : altDirContexts) { + obj = altDirContext.lookup("META-INF/resources/" + name); + if (obj != null) + return obj; + } + + // Really not found + throw new NamingException(sm.getString("resources.notFound", name)); } /** @@ -589,7 +659,31 @@ public abstract class BaseDirContext implements DirContext { return result.dirContext.listBindings(result.aliasName); } } - return doListBindings(name); + + // Next do a standard lookup + NamingEnumeration bindings = doListBindings(name); + + if (bindings != null) + return bindings; + + // Check the alternate locations + for (DirContext altDirContext : altDirContexts) { + if (altDirContext instanceof BaseDirContext) + bindings = ((BaseDirContext) altDirContext).doListBindings( + "META-INF/resources/" + name); + else { + try { + bindings = altDirContext.listBindings(name); + } catch (NamingException ne) { + // Ignore + } + } + if (bindings != null) + return bindings; + } + + // Really not found + throw new NamingException(sm.getString("resources.notFound", name)); } @@ -912,6 +1006,8 @@ public abstract class BaseDirContext implements DirContext { */ public final Attributes getAttributes(String name, String[] attrIds) throws NamingException { + + // First check for aliases if (!aliases.isEmpty()) { AliasResult result = findAlias(name); if (result.dirContext != null) { @@ -919,7 +1015,31 @@ public abstract class BaseDirContext implements DirContext { result.aliasName, attrIds); } } - return doGetAttributes(name, attrIds); + + // Next do a standard lookup + Attributes attrs = doGetAttributes(name, attrIds); + + if (attrs != null) + return attrs; + + // Check the alternate locations + for (DirContext altDirContext : altDirContexts) { + if (altDirContext instanceof BaseDirContext) + attrs = ((BaseDirContext) altDirContext).doGetAttributes( + "META-INF/resources/" + name, attrIds); + else { + try { + attrs = altDirContext.getAttributes(name, attrIds); + } catch (NamingException ne) { + // Ignore + } + } + if (attrs != null) + return attrs; + } + + // Really not found + throw new NamingException(sm.getString("resources.notFound", name)); } /** diff --git a/java/org/apache/naming/resources/FileDirContext.java b/java/org/apache/naming/resources/FileDirContext.java index 1f2c872da..ba02c1160 100644 --- a/java/org/apache/naming/resources/FileDirContext.java +++ b/java/org/apache/naming/resources/FileDirContext.java @@ -202,8 +202,7 @@ public class FileDirContext extends BaseDirContext { File file = file(name); if (file == null) - throw new NamingException - (sm.getString("resources.notFound", name)); + return null; if (file.isDirectory()) { FileDirContext tempContext = new FileDirContext(env); @@ -326,9 +325,8 @@ public class FileDirContext extends BaseDirContext { File file = file(name); if (file == null) - throw new NamingException - (sm.getString("resources.notFound", name)); - + return null; + return new NamingContextBindingsEnumeration(list(file).iterator(), this); @@ -432,8 +430,7 @@ public class FileDirContext extends BaseDirContext { File file = file(name); if (file == null) - throw new NamingException - (sm.getString("resources.notFound", name)); + return null; return new FileResourceAttributes(file); diff --git a/java/org/apache/naming/resources/WARDirContext.java b/java/org/apache/naming/resources/WARDirContext.java index efaa6c9be..2893d30a7 100644 --- a/java/org/apache/naming/resources/WARDirContext.java +++ b/java/org/apache/naming/resources/WARDirContext.java @@ -79,7 +79,8 @@ public class WARDirContext extends BaseDirContext { /** - * Constructor used for returning fake subcontexts. + * Constructor used for returning fake sub-contexts or for accessing + * META-INF/resources locations in bundled JAR files. */ protected WARDirContext(ZipFile base, Entry entries) { this.base = base; @@ -201,8 +202,8 @@ public class WARDirContext extends BaseDirContext { return this; Entry entry = treeLookup(name); if (entry == null) - throw new NamingException - (sm.getString("resources.notFound", name)); + return null; + ZipEntry zipEntry = entry.getEntry(); if (zipEntry.isDirectory()) return new WARDirContext(base, entry); @@ -320,8 +321,8 @@ public class WARDirContext extends BaseDirContext { this); Entry entry = treeLookup(name); if (entry == null) - throw new NamingException - (sm.getString("resources.notFound", name)); + return null; + return new NamingContextBindingsEnumeration(list(entry).iterator(), this); } @@ -441,8 +442,7 @@ public class WARDirContext extends BaseDirContext { else entry = treeLookup(name); if (entry == null) - throw new NamingException - (sm.getString("resources.notFound", name)); + return null; ZipEntry zipEntry = entry.getEntry(); @@ -841,7 +841,7 @@ public class WARDirContext extends BaseDirContext { /** * Entries structure. */ - protected class Entry implements Comparable { + protected static class Entry implements Comparable { // -------------------------------------------------------- Constructor -- 2.11.0