Index: java/org/apache/catalina/core/StandardContext.java =================================================================== --- java/org/apache/catalina/core/StandardContext.java (revision 1744611) +++ java/org/apache/catalina/core/StandardContext.java (working copy) @@ -84,6 +84,7 @@ import org.apache.catalina.ContainerListener; import org.apache.catalina.Context; import org.apache.catalina.CredentialHandler; +import org.apache.catalina.Engine; import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; @@ -2479,6 +2480,16 @@ this.threadBindingListener = threadBindingListener; } + public ExtensionValidator getExtensionValidator() { + try { + // Walking back through the Tomcat hierarchy. + // Host -> Engine -> Service -> Server. + return ((Engine)getParent().getParent()).getService().getServer().getExtensionValidator(); + } catch (Exception e) { + // Return default ExtensionValidator if any cast exception occurred. + return ExtensionValidator.DEFAULT; + } + } // ------------------------------------------------------ Public Properties @@ -5019,7 +5030,7 @@ // Validate required extensions boolean dependencyCheck = true; try { - dependencyCheck = ExtensionValidator.validateApplication + dependencyCheck = getExtensionValidator().validateApplication (getResources(), this); } catch (IOException ioe) { log.error(sm.getString("standardContext.extensionValidationError"), ioe); Index: java/org/apache/catalina/core/StandardServer.java =================================================================== --- java/org/apache/catalina/core/StandardServer.java (revision 1744611) +++ java/org/apache/catalina/core/StandardServer.java (working copy) @@ -173,6 +173,10 @@ private final Object namingToken = new Object(); + /** + * Extension validator that is used to ensure that extensions is fulfilled. + */ + private ExtensionValidator extensionValidator = new ExtensionValidator(); // ------------------------------------------------------------- Properties @@ -352,6 +356,11 @@ this.catalina = catalina; } + @Override + public ExtensionValidator getExtensionValidator(){ + return extensionValidator; + } + // --------------------------------------------------------- Server Methods @@ -855,7 +864,7 @@ File f = new File (url.toURI()); if (f.isFile() && f.getName().endsWith(".jar")) { - ExtensionValidator.addSystemResource(f); + extensionValidator.addSystemResource(f); } } catch (URISyntaxException e) { // Ignore Index: java/org/apache/catalina/Server.java =================================================================== --- java/org/apache/catalina/Server.java (revision 1744611) +++ java/org/apache/catalina/Server.java (working copy) @@ -22,6 +22,7 @@ import org.apache.catalina.deploy.NamingResourcesImpl; import org.apache.catalina.startup.Catalina; +import org.apache.catalina.util.ExtensionValidator; /** * A Server element represents the entire Catalina @@ -219,4 +220,6 @@ * context. */ public Object getNamingToken(); + + public ExtensionValidator getExtensionValidator(); } Index: java/org/apache/catalina/util/Extension.java =================================================================== --- java/org/apache/catalina/util/Extension.java (revision 1744611) +++ java/org/apache/catalina/util/Extension.java (working copy) @@ -248,11 +248,51 @@ } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Extension)) return false; - // -------------------------------------------------------- Private Methods + Extension extension = (Extension) o; + if (extensionName != null ? !extensionName.equals(extension.extensionName) + : extension.extensionName != null) + return false; + if (implementationURL != null ? !implementationURL.equals(extension.implementationURL) + : extension.implementationURL != null) + return false; + if (implementationVendor != null ? !implementationVendor.equals(extension.implementationVendor) + : extension.implementationVendor != null) + return false; + if (implementationVendorId != null ? !implementationVendorId.equals(extension.implementationVendorId) + : extension.implementationVendorId != null) + return false; + if (implementationVersion != null ? !implementationVersion.equals(extension.implementationVersion) + : extension.implementationVersion != null) + return false; + if (specificationVendor != null ? !specificationVendor.equals(extension.specificationVendor) + : extension.specificationVendor != null) + return false; + return specificationVersion != null ? specificationVersion.equals(extension.specificationVersion) + : extension.specificationVersion == null; + } + @Override + public int hashCode() { + int result = extensionName != null ? extensionName.hashCode() : 0; + result = 31 * result + (implementationURL != null ? implementationURL.hashCode() : 0); + result = 31 * result + (implementationVendor != null ? implementationVendor.hashCode() : 0); + result = 31 * result + (implementationVendorId != null ? implementationVendorId.hashCode() : 0); + result = 31 * result + (implementationVersion != null ? implementationVersion.hashCode() : 0); + result = 31 * result + (specificationVendor != null ? specificationVendor.hashCode() : 0); + result = 31 * result + (specificationVersion != null ? specificationVersion.hashCode() : 0); + return result; + } +// -------------------------------------------------------- Private Methods + + + /** * Return true if the first version number is greater than * or equal to the second; otherwise return false. Index: java/org/apache/catalina/util/ExtensionValidator.java =================================================================== --- java/org/apache/catalina/util/ExtensionValidator.java (revision 1744611) +++ java/org/apache/catalina/util/ExtensionValidator.java (working copy) @@ -20,9 +20,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.Locale; +import java.util.Set; import java.util.StringTokenizer; import java.util.jar.JarInputStream; import java.util.jar.Manifest; @@ -46,10 +47,17 @@ * @author Greg Murray * @author Justyna Horwat */ -public final class ExtensionValidator { +public class ExtensionValidator { private static final Log log = LogFactory.getLog(ExtensionValidator.class); + public static final ExtensionValidator DEFAULT = new ExtensionValidator(){ + @Override + public void addSystemResource(File jarFile) throws IOException { + throw new UnsupportedOperationException("this is a read-only ExtensionValidator"); + } + }; + /** * The string resources for this package. */ @@ -56,17 +64,17 @@ private static final StringManager sm = StringManager.getManager("org.apache.catalina.util"); - private static volatile ArrayList containerAvailableExtensions = + private volatile Set containerAvailableExtensions = null; - private static final ArrayList containerManifestResources = - new ArrayList<>(); + private final Set containerManifestResources = + new HashSet<>(); - // ----------------------------------------------------- Static Initializer + // ----------------------------------------------------- Constructor /** - * This static initializer loads the container level extensions that are + * This constructor loads the container level extensions that are * available to all web applications. This method scans all extension * directories available via the "java.ext.dirs" System property. * @@ -73,7 +81,7 @@ * The System Class-Path is also scanned for jar files that may contain * available extensions. */ - static { + public ExtensionValidator(){ // check for container level optional packages String systemClasspath = System.getProperty("java.class.path"); @@ -88,7 +96,7 @@ File item = new File(classpathItem); if (item.isFile()) { try { - addSystemResource(item); + doAddSystemResource(item); } catch (IOException e) { log.error(sm.getString ("extensionValidator.failload", item), e); @@ -123,13 +131,13 @@ * @return true if all required extensions satisfied * @throws IOException Error reading resources needed for validation */ - public static synchronized boolean validateApplication( + public synchronized boolean validateApplication( WebResourceRoot resources, Context context) throws IOException { String appName = context.getName(); - ArrayList appManifestResources = new ArrayList<>(); + Set appManifestResources = new HashSet<>(); // Web application manifest WebResource resource = resources.getResource("/META-INF/MANIFEST.MF"); @@ -171,15 +179,8 @@ * @param jarFile The system JAR whose manifest to add * @throws IOException Error reading JAR file */ - public static void addSystemResource(File jarFile) throws IOException { - try (InputStream is = new FileInputStream(jarFile)) { - Manifest manifest = getManifest(is); - if (manifest != null) { - ManifestResource mre = new ManifestResource(jarFile.getAbsolutePath(), manifest, - ManifestResource.SYSTEM); - containerManifestResources.add(mre); - } - } + public void addSystemResource(File jarFile) throws IOException { + doAddSystemResource(jarFile); } @@ -205,16 +206,16 @@ * * @return true if manifest resource file requirements are met */ - private static boolean validateManifestResources(String appName, - ArrayList resources) { + private boolean validateManifestResources(String appName, + Set resources) { boolean passes = true; int failureCount = 0; - ArrayList availableExtensions = null; + Set availableExtensions = null; Iterator it = resources.iterator(); while (it.hasNext()) { ManifestResource mre = it.next(); - ArrayList requiredList = mre.getRequiredExtensions(); + Set requiredList = mre.getRequiredExtensions(); if (requiredList == null) { continue; } @@ -299,21 +300,21 @@ * * @return HashMap Map of available extensions */ - private static ArrayList buildAvailableExtensionsList( - ArrayList resources) { + private Set buildAvailableExtensionsList( + Set resources) { - ArrayList availableList = null; + Set availableList = null; Iterator it = resources.iterator(); while (it.hasNext()) { ManifestResource mre = it.next(); - ArrayList list = mre.getAvailableExtensions(); + Set list = mre.getAvailableExtensions(); if (list != null) { Iterator values = list.iterator(); while (values.hasNext()) { Extension ext = values.next(); if (availableList == null) { - availableList = new ArrayList<>(); + availableList = new HashSet<>(); availableList.add(ext); } else { availableList.add(ext); @@ -331,7 +332,7 @@ * @param inStream Input stream to a WAR or JAR file * @return The WAR's or JAR's manifest */ - private static Manifest getManifest(InputStream inStream) throws IOException { + private Manifest getManifest(InputStream inStream) throws IOException { Manifest manifest = null; try (JarInputStream jin = new JarInputStream(inStream)) { manifest = jin.getManifest(); @@ -343,7 +344,7 @@ /** * Add the JARs specified to the extension list. */ - private static void addFolderList(String property) { + private void addFolderList(String property) { // get the files in the extensions directory String extensionsDir = System.getProperty(property); @@ -363,7 +364,7 @@ if (files[i].getName().toLowerCase(Locale.ENGLISH).endsWith(".jar") && files[i].isFile()) { try { - addSystemResource(files[i]); + doAddSystemResource(files[i]); } catch (IOException e) { log.error (sm.getString @@ -376,5 +377,19 @@ } - + /** + * + * @param jarFile + * @throws IOException + */ + private void doAddSystemResource(File jarFile) throws IOException { + try (InputStream is = new FileInputStream(jarFile)) { + Manifest manifest = getManifest(is); + if (manifest != null) { + ManifestResource mre = new ManifestResource(jarFile.getAbsolutePath(), manifest, + ManifestResource.SYSTEM); + containerManifestResources.add(mre); + } + } + } } Index: java/org/apache/catalina/util/ManifestResource.java =================================================================== --- java/org/apache/catalina/util/ManifestResource.java (revision 1744611) +++ java/org/apache/catalina/util/ManifestResource.java (working copy) @@ -16,8 +16,9 @@ */ package org.apache.catalina.util; -import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -37,8 +38,8 @@ public static final int WAR = 2; public static final int APPLICATION = 3; - private ArrayList availableExtensions = null; - private ArrayList requiredExtensions = null; + private Set availableExtensions = null; + private Set requiredExtensions = null; private final String resourceName; private final int resourceType; @@ -64,7 +65,7 @@ * * @return List of available extensions */ - public ArrayList getAvailableExtensions() { + public Set getAvailableExtensions() { return availableExtensions; } @@ -73,7 +74,7 @@ * * @return List of required extensions */ - public ArrayList getRequiredExtensions() { + public Set getRequiredExtensions() { return requiredExtensions; } @@ -136,9 +137,36 @@ return (sb.toString()); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ManifestResource)) return false; - // -------------------------------------------------------- Private Methods + ManifestResource that = (ManifestResource) o; + if (resourceType != that.resourceType) return false; + if (availableExtensions != null ? !availableExtensions.equals(that.availableExtensions) + : that.availableExtensions != null) + return false; + if (requiredExtensions != null ? !requiredExtensions.equals(that.requiredExtensions) + : that.requiredExtensions != null) + return false; + + return resourceName != null ? resourceName.equals(that.resourceName) + : that.resourceName == null; + } + + @Override + public int hashCode() { + int result = availableExtensions != null ? availableExtensions.hashCode() : 0; + result = 31 * result + (requiredExtensions != null ? requiredExtensions.hashCode() : 0); + result = 31 * result + (resourceName != null ? resourceName.hashCode() : 0); + result = 31 * result + resourceType; + return result; + } + +// -------------------------------------------------------- Private Methods + private void processManifest(Manifest manifest) { availableExtensions = getAvailableExtensions(manifest); requiredExtensions = getRequiredExtensions(manifest); @@ -154,7 +182,7 @@ * @return List of required extensions, or null if the application * does not require any extensions */ - private ArrayList getRequiredExtensions(Manifest manifest) { + private Set getRequiredExtensions(Manifest manifest) { Attributes attributes = manifest.getMainAttributes(); String names = attributes.getValue("Extension-List"); @@ -161,7 +189,7 @@ if (names == null) return null; - ArrayList extensionList = new ArrayList<>(); + Set extensionList = new HashSet<>(); names += " "; while (true) { @@ -201,7 +229,7 @@ * @return List of available extensions, or null if the web application * does not bundle any extensions */ - private ArrayList getAvailableExtensions(Manifest manifest) { + private Set getAvailableExtensions(Manifest manifest) { Attributes attributes = manifest.getMainAttributes(); String name = attributes.getValue("Extension-Name"); @@ -208,7 +236,7 @@ if (name == null) return null; - ArrayList extensionList = new ArrayList<>(); + Set extensionList = new HashSet<>(); Extension extension = new Extension(); extension.setExtensionName(name);