Return-Path: Delivered-To: apmail-jakarta-tomcat-dev-archive@apache.org Received: (qmail 65623 invoked from network); 24 Aug 2002 02:28:13 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 24 Aug 2002 02:28:13 -0000 Received: (qmail 16146 invoked by uid 97); 24 Aug 2002 02:28:11 -0000 Delivered-To: qmlist-jakarta-archive-tomcat-dev@jakarta.apache.org Received: (qmail 16109 invoked by uid 97); 24 Aug 2002 02:28:10 -0000 Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Tomcat Developers List" Reply-To: "Tomcat Developers List" Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 16098 invoked by uid 97); 24 Aug 2002 02:28:09 -0000 X-Antivirus: nagoya (v4218 created Aug 14 2002) Date: 24 Aug 2002 02:27:28 -0000 Message-ID: <20020824022728.86871.qmail@icarus.apache.org> From: horwat@apache.org To: jakarta-tomcat-catalina-cvs@apache.org Subject: cvs commit: jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util Extension.java ExtensionValidator.java ManifestResource.java LocalStrings.properties X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N horwat 2002/08/23 19:27:28 Modified: catalina/src/share/org/apache/catalina/loader StandardClassLoader.java WebappClassLoader.java WebappLoader.java catalina/src/share/org/apache/catalina/startup Bootstrap.java BootstrapService.java ContextConfig.java Tool.java catalina/src/share/org/apache/catalina/core StandardContext.java catalina/src/share/org/apache/catalina/util LocalStrings.properties Added: catalina/src/share/org/apache/catalina/util Extension.java ExtensionValidator.java ManifestResource.java Removed: catalina/src/share/org/apache/catalina/loader Extension.java Log: Implementation of extension version checking as defined by the Optional Package Versioning mechanism (http://java.sun.com/j2se/1.4/docs/guide/extensions). During deployment, declared dependencies in manifest entries of library JARs and WARs are now recognized. If these dependencies are not satisfied, the applicati on is rejected with an informative log message. Removed the broken dependency checking mechanism. Submitted by: Justyna Horwat and Greg Murray Revision Changes Path 1.3 +4 -120 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/loader/StandardClassLoader.java Index: StandardClassLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/loader/StandardClassLoader.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- StandardClassLoader.java 7 Aug 2002 15:00:36 -0000 1.2 +++ StandardClassLoader.java 24 Aug 2002 02:27:27 -0000 1.3 @@ -249,15 +249,6 @@ /** - * The set of optional packages (formerly standard extensions) that - * are available in the repositories associated with this class loader. - * Each object in this list is of type - * org.apache.catalina.loader.Extension. - */ - protected ArrayList available = new ArrayList(); - - - /** * The debugging detail level of this component. */ protected int debug = 0; @@ -282,15 +273,6 @@ /** - * The set of optional packages (formerly standard extensions) that - * are required in the repositories associated with this class loader. - * Each object in this list is of type - * org.apache.catalina.loader.Extension. - */ - protected ArrayList required = new ArrayList(); - - - /** * A list of read File and Jndi Permission's required if this loader * is for a web application context. */ @@ -445,41 +427,6 @@ /** - * Return a list of "optional packages" (formerly "standard extensions") - * that have been declared to be available in the repositories associated - * with this class loader, plus any parent class loader implemented with - * the same class. - */ - public Extension[] findAvailable() { - - // Initialize the results with our local available extensions - ArrayList results = new ArrayList(); - Iterator available = this.available.iterator(); - while (available.hasNext()) - results.add(available.next()); - - // Trace our parentage tree and add declared extensions when possible - ClassLoader loader = this; - while (true) { - loader = loader.getParent(); - if (loader == null) - break; - if (!(loader instanceof StandardClassLoader)) - continue; - Extension extensions[] = - ((StandardClassLoader) loader).findAvailable(); - for (int i = 0; i < extensions.length; i++) - results.add(extensions[i]); - } - - // Return the results as an array - Extension extensions[] = new Extension[results.size()]; - return ((Extension[]) results.toArray(extensions)); - - } - - - /** * Return a String array of the current repositories for this class * loader. If there are no repositories, a zero-length array is * returned. @@ -492,41 +439,6 @@ /** - * Return a list of "optional packages" (formerly "standard extensions") - * that have been declared to be required in the repositories associated - * with this class loader, plus any parent class loader implemented with - * the same class. - */ - public Extension[] findRequired() { - - // Initialize the results with our local required extensions - ArrayList results = new ArrayList(); - Iterator required = this.required.iterator(); - while (required.hasNext()) - results.add(required.next()); - - // Trace our parentage tree and add declared extensions when possible - ClassLoader loader = this; - while (true) { - loader = loader.getParent(); - if (loader == null) - break; - if (!(loader instanceof StandardClassLoader)) - continue; - Extension extensions[] = - ((StandardClassLoader) loader).findRequired(); - for (int i = 0; i < extensions.length; i++) - results.add(extensions[i]); - } - - // Return the results as an array - Extension extensions[] = new Extension[results.size()]; - return ((Extension[]) results.toArray(extensions)); - - } - - - /** * This class loader doesn't check for reloading. */ public boolean modified() { @@ -542,13 +454,6 @@ public String toString() { StringBuffer sb = new StringBuffer("StandardClassLoader\r\n"); - sb.append(" available:\r\n"); - Iterator available = this.available.iterator(); - while (available.hasNext()) { - sb.append(" "); - sb.append(available.next().toString()); - sb.append("\r\n"); - } sb.append(" delegate: "); sb.append(delegate); sb.append("\r\n"); @@ -558,13 +463,6 @@ sb.append(repositories[i]); sb.append("\r\n"); } - sb.append(" required:\r\n"); - Iterator required = this.required.iterator(); - while (required.hasNext()) { - sb.append(" "); - sb.append(required.next().toString()); - sb.append("\r\n"); - } if (this.parent != null) { sb.append("----------> Parent Classloader:\r\n"); sb.append(this.parent.toString()); @@ -1090,20 +988,6 @@ throw new IllegalArgumentException ("addRepositoryInternal: Invalid URL '" + repository + "'"); - } - if (!((manifest == null) && (jarFile == null))) { - if ((manifest == null) && (jarFile != null)) - manifest = jarFile.getManifest(); - if (manifest != null) { - Iterator extensions = - Extension.getAvailable(manifest).iterator(); - while (extensions.hasNext()) - available.add(extensions.next()); - extensions = - Extension.getRequired(manifest).iterator(); - while (extensions.hasNext()) - required.add(extensions.next()); - } } } catch (Throwable t) { t.printStackTrace(); 1.5 +4 -121 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java Index: WebappClassLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- WebappClassLoader.java 14 Aug 2002 20:12:54 -0000 1.4 +++ WebappClassLoader.java 24 Aug 2002 02:27:27 -0000 1.5 @@ -248,15 +248,6 @@ /** - * The set of optional packages (formerly standard extensions) that - * are available in the repositories associated with this class loader. - * Each object in this list is of type - * org.apache.catalina.loader.Extension. - */ - protected ArrayList available = new ArrayList(); - - - /** * The cache of ResourceEntry for classes and resources we have loaded, * keyed by resource name. */ @@ -343,15 +334,6 @@ /** - * The set of optional packages (formerly standard extensions) that - * are required in the repositories associated with this class loader. - * Each object in this list is of type - * org.apache.catalina.loader.Extension. - */ - protected ArrayList required = new ArrayList(); - - - /** * A list of read File and Jndi Permission's required if this loader * is for a web application context. */ @@ -680,55 +662,6 @@ } result4[jarRealFiles.length] = file; jarRealFiles = result4; - - // Load manifest - Manifest manifest = jarFile.getManifest(); - if (manifest != null) { - Iterator extensions = Extension.getAvailable(manifest).iterator(); - while (extensions.hasNext()) { - available.add(extensions.next()); - } - extensions = Extension.getRequired(manifest).iterator(); - while (extensions.hasNext()) { - required.add(extensions.next()); - } - } - - } - - - /** - * Return a list of "optional packages" (formerly "standard extensions") - * that have been declared to be available in the repositories associated - * with this class loader, plus any parent class loader implemented with - * the same class. - */ - public Extension[] findAvailable() { - - // Initialize the results with our local available extensions - ArrayList results = new ArrayList(); - Iterator available = this.available.iterator(); - while (available.hasNext()) - results.add(available.next()); - - // Trace our parentage tree and add declared extensions when possible - ClassLoader loader = this; - while (true) { - loader = loader.getParent(); - if (loader == null) - break; - if (!(loader instanceof WebappClassLoader)) - continue; - Extension extensions[] = - ((WebappClassLoader) loader).findAvailable(); - for (int i = 0; i < extensions.length; i++) - results.add(extensions[i]); - } - - // Return the results as an array - Extension extensions[] = new Extension[results.size()]; - return ((Extension[]) results.toArray(extensions)); - } @@ -745,41 +678,6 @@ /** - * Return a list of "optional packages" (formerly "standard extensions") - * that have been declared to be required in the repositories associated - * with this class loader, plus any parent class loader implemented with - * the same class. - */ - public Extension[] findRequired() { - - // Initialize the results with our local required extensions - ArrayList results = new ArrayList(); - Iterator required = this.required.iterator(); - while (required.hasNext()) - results.add(required.next()); - - // Trace our parentage tree and add declared extensions when possible - ClassLoader loader = this; - while (true) { - loader = loader.getParent(); - if (loader == null) - break; - if (!(loader instanceof WebappClassLoader)) - continue; - Extension extensions[] = - ((WebappClassLoader) loader).findRequired(); - for (int i = 0; i < extensions.length; i++) - results.add(extensions[i]); - } - - // Return the results as an array - Extension extensions[] = new Extension[results.size()]; - return ((Extension[]) results.toArray(extensions)); - - } - - - /** * Have one or more classes or resources been modified so that a reload * is appropriate? */ @@ -878,13 +776,6 @@ public String toString() { StringBuffer sb = new StringBuffer("WebappClassLoader\r\n"); - sb.append(" available:\r\n"); - Iterator available = this.available.iterator(); - while (available.hasNext()) { - sb.append(" "); - sb.append(available.next().toString()); - sb.append("\r\n"); - } sb.append(" delegate: "); sb.append(delegate); sb.append("\r\n"); @@ -894,13 +785,6 @@ sb.append(repositories[i]); sb.append("\r\n"); } - sb.append(" required:\r\n"); - Iterator required = this.required.iterator(); - while (required.hasNext()) { - sb.append(" "); - sb.append(required.next().toString()); - sb.append("\r\n"); - } if (this.parent != null) { sb.append("----------> Parent Classloader:\r\n"); sb.append(this.parent.toString()); @@ -1566,7 +1450,6 @@ paths = new String[0]; hasExternalRepositories = false; - required.clear(); permissionList.clear(); loaderPC.clear(); 1.3 +4 -47 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/loader/WebappLoader.java Index: WebappLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/loader/WebappLoader.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- WebappLoader.java 22 Aug 2002 17:02:12 -0000 1.2 +++ WebappLoader.java 24 Aug 2002 02:27:27 -0000 1.3 @@ -646,9 +646,6 @@ throw new LifecycleException("start: ", t); } - // Validate that all required packages are actually available - validatePackages(); - // Start our background thread if we are reloadable if (reloadable) { log.info(sm.getString("webappLoader.reloading")); @@ -1282,46 +1279,6 @@ } thread = null; - - } - - - /** - * Validate that the required optional packages for this application - * are actually present. - * - * @exception LifecycleException if a required package is not available - */ - private void validatePackages() throws LifecycleException { - - ClassLoader classLoader = getClassLoader(); - if (classLoader instanceof WebappClassLoader) { - - Extension available[] = - ((WebappClassLoader) classLoader).findAvailable(); - Extension required[] = - ((WebappClassLoader) classLoader).findRequired(); - if (log.isDebugEnabled()) - log.debug("Optional Packages: available=" + - available.length + ", required=" + - required.length); - - for (int i = 0; i < required.length; i++) { - if (log.isDebugEnabled()) - log.debug("Checking for required package " + required[i]); - boolean found = false; - for (int j = 0; j < available.length; j++) { - if (available[j].isCompatibleWith(required[i])) { - found = true; - break; - } - } - if (!found) - throw new LifecycleException - ("Missing optional package " + required[i]); - } - - } } 1.2 +4 -5 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/Bootstrap.java Index: Bootstrap.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/Bootstrap.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Bootstrap.java 18 Jul 2002 16:47:47 -0000 1.1 +++ Bootstrap.java 24 Aug 2002 02:27:27 -0000 1.2 @@ -71,7 +71,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import org.apache.catalina.loader.Extension; import org.apache.catalina.loader.StandardClassLoader; 1.2 +4 -5 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/BootstrapService.java Index: BootstrapService.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/BootstrapService.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- BootstrapService.java 18 Jul 2002 16:47:48 -0000 1.1 +++ BootstrapService.java 24 Aug 2002 02:27:27 -0000 1.2 @@ -73,7 +73,6 @@ import java.util.ArrayList; import org.apache.commons.daemon.Daemon; import org.apache.commons.daemon.DaemonContext; -import org.apache.catalina.loader.Extension; import org.apache.catalina.loader.StandardClassLoader; 1.11 +4 -5 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ContextConfig.java Index: ContextConfig.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ContextConfig.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- ContextConfig.java 21 Aug 2002 03:24:37 -0000 1.10 +++ ContextConfig.java 24 Aug 2002 02:27:27 -0000 1.11 @@ -122,7 +122,6 @@ import org.apache.catalina.deploy.FilterMap; import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.SecurityConstraint; -import org.apache.catalina.loader.Extension; import org.apache.catalina.util.StringManager; import org.apache.catalina.util.SchemaResolver; import org.apache.catalina.valves.ValveBase; 1.2 +4 -5 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/Tool.java Index: Tool.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/Tool.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Tool.java 18 Jul 2002 16:47:50 -0000 1.1 +++ Tool.java 24 Aug 2002 02:27:27 -0000 1.2 @@ -71,7 +71,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import org.apache.catalina.loader.Extension; import org.apache.catalina.loader.StandardClassLoader; 1.7 +14 -4 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/core/StandardContext.java Index: StandardContext.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/core/StandardContext.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- StandardContext.java 23 Aug 2002 14:54:48 -0000 1.6 +++ StandardContext.java 24 Aug 2002 02:27:27 -0000 1.7 @@ -142,6 +142,7 @@ import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.session.StandardManager; import org.apache.catalina.util.CharsetMapper; +import org.apache.catalina.util.ExtensionValidator; import org.apache.catalina.util.RequestUtil; @@ -3561,6 +3562,15 @@ // Post work directory postWorkDirectory(); + + // Validate required extensions + ExtensionValidator validator = ExtensionValidator.getInstance(); + boolean dependencyCheck = validator.validateApplication( + getResources(), this); + if (!dependencyCheck) { + // do not make application available if depency check fails + ok = false; + } // Reading the "catalina.useNaming" environment variable String useNamingProperty = System.getProperty("catalina.useNaming"); 1.2 +4 -0 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/LocalStrings.properties,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- LocalStrings.properties 18 Jul 2002 16:47:46 -0000 1.1 +++ LocalStrings.properties 24 Aug 2002 02:27:28 -0000 1.2 @@ -2,4 +2,8 @@ resourceSet.locked=No modifications are allowed to a locked ResourceSet hexUtil.bad=Bad hexadecimal digit hexUtil.odd=Odd number of hexadecimal digits +#Default Messages Utilized by the ExtensionValidator +extensionValidator.web-application-manifest=Web Application Manifest +extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension "{2}" not found. +extensionValidator.extension-validation-error=ExtensionValidator[{0}]: Failure to find {1} required extension(s). 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/Extension.java Index: Extension.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/Extension.java,v 1.1 2002/08/24 02:27:28 horwat Exp $ * $Revision: 1.1 $ * $Date: 2002/08/24 02:27:28 $ * * ==================================================================== * * 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 * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.util; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.jar.Attributes; import java.util.jar.Manifest; /** * Utility class that represents either an available "Optional Package" * (formerly known as "Standard Extension") as described in the manifest * of a JAR file, or the requirement for such an optional package. It is * used to support the requirements of the Servlet Specification, version * 2.3, related to providing shared extensions to all webapps. *

* In addition, static utility methods are available to scan a manifest * and return an array of either available or required optional modules * documented in that manifest. *

* For more information about optional packages, see the document * Optional Package Versioning in the documentation bundle for your * Java2 Standard Edition package, in file * guide/extensions/versioning.html. * * @author Craig McClanahan * @author Justyna Horwat * @author Greg Murray * @version $Revision: 1.1 $ $Date: 2002/08/24 02:27:28 $ */ public final class Extension { // ------------------------------------------------------------- Properties /** * The name of the optional package being made available, or required. */ private String extensionName = null; public String getExtensionName() { return (this.extensionName); } public void setExtensionName(String extensionName) { this.extensionName = extensionName; } /** * UniqueId created by combining the extension name and implementation * version. */ public String getUniqueId() { return this.extensionName + this.implementationVersion; } /** * The URL from which the most recent version of this optional package * can be obtained if it is not already installed. */ private String implementationURL = null; public String getImplementationURL() { return (this.implementationURL); } public void setImplementationURL(String implementationURL) { this.implementationURL = implementationURL; } /** * The name of the company or organization that produced this * implementation of this optional package. */ private String implementationVendor = null; public String getImplementationVendor() { return (this.implementationVendor); } public void setImplementationVendor(String implementationVendor) { this.implementationVendor = implementationVendor; } /** * The unique identifier of the company that produced the optional * package contained in this JAR file. */ private String implementationVendorId = null; public String getImplementationVendorId() { return (this.implementationVendorId); } public void setImplementationVendorId(String implementationVendorId) { this.implementationVendorId = implementationVendorId; } /** * The version number (dotted decimal notation) for this implementation * of the optional package. */ private String implementationVersion = null; public String getImplementationVersion() { return (this.implementationVersion); } public void setImplementationVersion(String implementationVersion) { this.implementationVersion = implementationVersion; } /** * The name of the company or organization that originated the * specification to which this optional package conforms. */ private String specificationVendor = null; public String getSpecificationVendor() { return (this.specificationVendor); } public void setSpecificationVendor(String specificationVendor) { this.specificationVendor = specificationVendor; } /** * The version number (dotted decimal notation) of the specification * to which this optional package conforms. */ private String specificationVersion = null; public String getSpecificationVersion() { return (this.specificationVersion); } public void setSpecificationVersion(String specificationVersion) { this.specificationVersion = specificationVersion; } /** * fulfilled is true if all the required extension dependencies have been * satisfied */ private boolean fulfilled = false; public void setFulfilled(boolean fulfilled) { this.fulfilled = fulfilled; } public boolean isFulfilled() { return fulfilled; } // --------------------------------------------------------- Public Methods /** * Return true if the specified Extension * (which represents an optional package required by this application) * is satisfied by this Extension (which represents an * optional package that is already installed. Otherwise, return * false. * * @param required Extension of the required optional package */ public boolean isCompatibleWith(Extension required) { // Extension Name must match if (extensionName == null) return (false); if (!extensionName.equals(required.getExtensionName())) return (false); // Available specification version must be >= required if (!isNewer(specificationVersion, required.getSpecificationVersion())) return (false); // Implementation Vendor ID must match if (implementationVendorId == null) return (false); if (!implementationVendorId.equals(required.getImplementationVendorId())) return (false); // Implementation version must be >= required if (!isNewer(implementationVersion, required.getImplementationVersion())) return (false); // This available optional package satisfies the requirements return (true); } /** * Return a String representation of this object. */ public String toString() { StringBuffer sb = new StringBuffer("Extension["); sb.append(extensionName); if (implementationURL != null) { sb.append(", implementationURL="); sb.append(implementationURL); } if (implementationVendor != null) { sb.append(", implementationVendor="); sb.append(implementationVendor); } if (implementationVendorId != null) { sb.append(", implementationVendorId="); sb.append(implementationVendorId); } if (implementationVersion != null) { sb.append(", implementationVersion="); sb.append(implementationVersion); } if (specificationVendor != null) { sb.append(", specificationVendor="); sb.append(specificationVendor); } if (specificationVersion != null) { sb.append(", specificationVersion="); sb.append(specificationVersion); } sb.append("]"); return (sb.toString()); } // -------------------------------------------------------- Private Methods /** * Return true if the first version number is greater than * or equal to the second; otherwise return false. * * @param first First version number (dotted decimal) * @param second Second version number (dotted decimal) * * @exception NumberFormatException on a malformed version number */ private boolean isNewer(String first, String second) throws NumberFormatException { if ((first == null) || (second == null)) return (false); if (first.equals(second)) return (true); StringTokenizer fTok = new StringTokenizer(first, ".", true); StringTokenizer sTok = new StringTokenizer(second, ".", true); int fVersion = 0; int sVersion = 0; while (fTok.hasMoreTokens() || sTok.hasMoreTokens()) { if (fTok.hasMoreTokens()) fVersion = Integer.parseInt(fTok.nextToken()); else fVersion = 0; if (sTok.hasMoreTokens()) sVersion = Integer.parseInt(sTok.nextToken()); else sVersion = 0; if (fVersion < sVersion) return (false); else if (fVersion > sVersion) return (true); if (fTok.hasMoreTokens()) // Swallow the periods fTok.nextToken(); if (sTok.hasMoreTokens()) sTok.nextToken(); } return (true); // Exact match } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/ExtensionValidator.java Index: ExtensionValidator.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/ExtensionValidator.java,v 1.1 2002/08/24 02:27:28 horwat Exp $ * $Revision: 1.1 $ * $Date: 2002/08/24 02:27:28 $ * * ==================================================================== * * 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 * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.util; import java.util.StringTokenizer; import java.util.ArrayList; import java.util.Iterator; import java.util.HashMap; import java.util.ResourceBundle; import java.text.MessageFormat; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.jar.Attributes; import org.apache.catalina.core.StandardContext; import org.apache.catalina.Logger; import org.apache.naming.resources.BaseDirContext; import org.apache.naming.resources.Resource; // JNDI Imports import javax.naming.NamingEnumeration; import javax.naming.directory.DirContext; import javax.naming.Binding; /** * Ensures that all extension dependies are resolved for a WEB application * are met. This class builds a master list of extensions available to an * applicaiton and then validates those extensions. * * See http://java.sun.com/j2se/1.4/docs/guide/extensions/spec.html for * a detailed explanation of the extension mechanism in Java. * * @author Greg Murray * @author Justyna Horwat * @version $Revision: 1.1 $ $Date: 2002/08/24 02:27:28 $ * */ public final class ExtensionValidator { // ------------------------------------------------------------- Properties private static ExtensionValidator validator = null; private static HashMap containerAvailableExtensions = null; private static ArrayList containerManifestResources = null; private static ResourceBundle messages = null; /* * Access to this class can only be made through the factory method * getInstance() * * This private constructor loads the container level extensions that are * available to all web applications. This method scans all extension * directories available to via the "java.ext.dirs" System property. * * The System Class-Path is also scanned for jar files that may contain * available extensions. The system extensions are loaded only the * first time an instance of the ExtensionValidator is created. */ private ExtensionValidator() { // load the container level extensions containerManifestResources = new ArrayList(); // check for container level optional packages String systemClasspath = System.getProperties(). getProperty("java.class.path"); StringTokenizer strTok = new StringTokenizer(systemClasspath, File.pathSeparator); ArrayList items = new ArrayList(); // build a list of jar files in the classpath while (strTok.hasMoreTokens()) { String classpathItem = strTok.nextToken(); if (classpathItem.toLowerCase().endsWith(".jar")) { items.add(classpathItem); } } // get the files in the extensions directory String extensionsDir = System.getProperties(). getProperty("java.ext.dirs"); StringTokenizer extensionsTok = null; if (extensionsDir != null) { extensionsTok = new StringTokenizer(extensionsDir, File.pathSeparator); } while ((extensionsTok != null) && extensionsTok.hasMoreTokens()) { String targetDir = extensionsTok.nextToken(); // check if the directory exits if (((new File(targetDir)).exists()) && (new File(targetDir)).isDirectory()) { // get a file list File[] files = (new File(targetDir)).listFiles(); // see if any file is a jar file for (int loop = 0; loop < files.length; loop++) { if (files[loop].getName().toLowerCase().endsWith(".jar")) { items.add(files[loop].getAbsolutePath()); Manifest manifest = getManifest(files[loop]); if (manifest != null) { ManifestResource mre = new ManifestResource (files[loop].getAbsolutePath(), manifest, ManifestResource.SYSTEM); containerManifestResources.add(mre); } } } } } } // --------------------------------------------------------- Public Methods /** * Runtime validation of a Web Applicaiton. * * This method uses JNDI to look up the resources located under a * DirContext. It locates Web Application MANIFEST.MF * file in the /META-INF/ directory of the application and all * MANIFEST.MF files in each JAR file located in the WEB-INF/lib * directory and creates an ArrayList of * ManifestResorce objects. These objects are then passed * to the validateManifestResources method for validation. * * @param DirContext The JNDI root of the Web Application * @param StandardContext The context from which the Logger and path * to the application * * @return true if all required extensions satisfied * */ public static synchronized boolean validateApplication( DirContext dirContext, StandardContext context) { String appName = context.getPath(); Logger logger = context.getLogger(); ArrayList appManifestResources = new ArrayList(); ManifestResource appManifestResource = null; // If the application context is null it does not exist and // therefore is not valid if (dirContext == null) return false; // Find the Mainfest for the Web Applicaiton try { NamingEnumeration wne = null; wne = dirContext.listBindings("/META-INF/"); Binding binding = (Binding)wne.nextElement(); if (binding.getName().toUpperCase().equals("MANIFEST.MF")) { Resource resource = (Resource)dirContext.lookup ("/META-INF/" + binding.getName()); InputStream inputStream = resource.streamContent(); Manifest manifest = new Manifest(inputStream); ManifestResource mre = new ManifestResource (getMessage("extensionValidator.web-application-manifest", logger), manifest, ManifestResource.WAR); appManifestResources.add(mre); } } catch (javax.naming.NamingException nex) { // Application does not contain a MANIFEST.MF file } catch (java.io.IOException iox) { // Unable to load MANIFEST.MF file } // Locate the Manifests for all necessary Jar Files in the Application ArrayList jarEntries = new ArrayList(); NamingEnumeration ne = null; try { if (dirContext != null) ne = dirContext.listBindings("WEB-INF/lib/"); while ((ne != null) && ne.hasMoreElements()) { Binding binding = (Binding)ne.nextElement(); if (binding.getName().toLowerCase().endsWith(".jar")) { Resource resource = (Resource)dirContext.lookup ("/WEB-INF/lib/" + binding.getName()); try { InputStream in = resource.streamContent(); JarInputStream jin = new JarInputStream(in); Manifest jmanifest = jin.getManifest(); ManifestResource mre = new ManifestResource (binding.getName(), jmanifest, ManifestResource.APPLICATION); appManifestResources.add(mre); } catch (java.io.IOException iox) { // do not do anything... go to the next entry } } } } catch (javax.naming.NamingException nex) { // Jump out of the check for this application because it // has no resources } return validateManifestResources(appName,appManifestResources, logger); } /** * Return an instance of the ExtensionValidator. * The ExtensionValidator is a singleton. */ public static ExtensionValidator getInstance() { if (validator == null) { validator = new ExtensionValidator(); } return validator; } // -------------------------------------------------------- Private Methods /** * Validates a ArrayList of ManifestResource * objects. This method requires an application name (which is the * context root of the application at runtime). * * A Logger is required for the output of error messages. * false is returned if the extension depeendencies * represented by any given ManifestResource objects * is not met. * * This method should also provide static validation of a Web Applicaiton * if provided with the necessary parameters. * * @param String The name of the Application that will appear in the * error messages * @param ArrayList A list of ManifestResource objects * to be validated. * @param Logger A logger to which failure messages are logged. * * @return true if manifest resource file requirements are met * */ private static boolean validateManifestResources(String appName, ArrayList resources, Logger logger) { boolean passes = true; int failureCount = 0; HashMap availableExtensions = null; Iterator it = resources.iterator(); // iterate through the list while (it.hasNext()) { ManifestResource mre = (ManifestResource)it.next(); // check if the resource requires extensions if (mre.requiresExtensions()) { // build the list of available extensions if necessary if (availableExtensions == null) { availableExtensions = buildAvailableExtensionsMap(resources); } // load the container level resource map if it has not // been built yet if (containerAvailableExtensions == null) { containerAvailableExtensions = buildAvailableExtensionsMap( containerManifestResources); } // get a list of the required extensions ArrayList requiredList = mre.getRequiredExtensions(); Iterator rit = requiredList.iterator(); // iterate through the list of required extensions while (rit.hasNext()) { Extension requiredExtension = (Extension)rit.next(); String key = requiredExtension.getUniqueId(); // check in the applicaion first for the extension if (availableExtensions.containsKey(key)) { // check if the desired extension is compatible // with the required extension Extension targetExtension = (Extension) ((ManifestResource)availableExtensions.get(key)). getAvailableExtensions().get(key); // check if the desired extension is valid if (targetExtension.isCompatibleWith(requiredExtension)) { // extension requirements have passed requiredExtension.setFulfilled(true); } // check the container level list for the extension } else if (containerAvailableExtensions.containsKey(key)) { // check if the desired extension is compatible // with the required extension Extension targetExtension = (Extension) ((ManifestResource)containerAvailableExtensions. get(key)).getAvailableExtensions().get(key); // check if the desired extension is valid if (targetExtension.isCompatibleWith(requiredExtension)) { // extension requirements have passed requiredExtension.setFulfilled(true); } } else { // FAILURE has occured String[] args = {appName, mre.getResourceName(), requiredExtension.getExtensionName() }; logMessage("extensionValidator.extension-not-found-error", args, logger); passes = false; failureCount++; } } } } if (!passes) { String[] args = {appName,failureCount + "" }; logMessage("extensionValidator.extension-validation-error", args, logger); } return passes; } /* * Build this list of available extensions so that we do not have to * re-build this list every time we iterate through the list of required * extensions. All available extensions in all of the * MainfestResource objects will be added to a * HashMapwhich is returned on the first dependency list * processing pass. * * The key is the name + implementation version. * * NOTE: A list is built only if there is a dependency that needs * to be checked (performace optimization). * * @param ArrayList A list of ManifestResource objects * * @return HashMap Map of available extensions */ private static HashMap buildAvailableExtensionsMap(ArrayList resources) { HashMap availableMap = new HashMap(); Iterator it = resources.iterator(); // iterate through the list while (it.hasNext()) { ManifestResource mre = (ManifestResource)it.next(); if (mre.requiresExtensions()) { HashMap map = mre.getAvailableExtensions(); Iterator kit = map.keySet().iterator(); while (kit.hasNext()) { String key = (String)kit.next(); Extension ext = (Extension)map.get(key); // mre is needed for error reporting if a match is not made // it has access to the extensions if (!availableMap.containsKey(key)) { availableMap.put(ext.getUniqueId(), mre); } } } } return availableMap; } /** * Return the Manifest from a jar file or war file * * @param File is a war file or a jar file * @return java.util.jar.Manifest * */ private static Manifest getManifest(File file) { try { FileInputStream fis = new FileInputStream(file); JarInputStream jin = new JarInputStream(fis); return jin.getManifest(); } catch (java.io.IOException iox) { return null; } } /** * Standardized method of logging localized messages by the * ExtensionValidator. * * @param String The key of the message in the ResourceBundle * @param String[] The Arguments to be applied to the messages * if any (null) if none * @param Logger The logger to which the messages will be logged * */ private static void logMessage(String messageId, String[] args, Logger logger) { String message = getMessage(messageId, logger); if (args != null) { logger.log(MessageFormat.format(message, args)); } else { logger.log(message); } } /** * Standardized method of obtaining a localized message. * * @param String The key of the message in the ResourceBundle * @param Logger The logger to which error messages encounter looking * up resources will be logged * * @return String message string */ private static String getMessage(String messageId, Logger logger) { // load localized messages if necessary if (messages == null) { try { messages = ResourceBundle.getBundle( "org.apache.catalina.util.LocalStrings"); } catch (java.util.MissingResourceException mrx) { logger.log( "Unable to load localized resources for ExtensionValidator"); // jump out of method return null; } } String message = null; try { return messages.getString(messageId); } catch (java.util.MissingResourceException mrx) { logger.log("Unable to load resources for ExtensionValidator"); return null; } } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/ManifestResource.java Index: ManifestResource.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/util/ManifestResource.java,v 1.1 2002/08/24 02:27:28 horwat Exp $ * $Revision: 1.1 $ * $Date: 2002/08/24 02:27:28 $ * ==================================================================== * * 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 * . * */ package org.apache.catalina.util; import java.util.HashMap; import java.util.Iterator; import java.util.jar.Manifest; import java.util.jar.Attributes; import java.util.ArrayList; /** * Representation of a Manifest file and its available extenstions and * required extensions * * @author Greg Murray * @author Justyna Horwat * @version $Revision: 1.1 $ $Date: 2002/08/24 02:27:28 $ * */ public class ManifestResource { // ------------------------------------------------------------- Properties // These are the resource types for determining effect error messages public static final int SYSTEM = 1; public static final int WAR = 2; public static final int APPLICATION = 3; private HashMap availableExtensions = null; private ArrayList requiredExtensions = null; private String resourceName = null; private int resourceType = -1; public ManifestResource(String resourceName, Manifest manifest, int resourceType) { this.resourceName = resourceName; this.resourceType = resourceType; processManifest(manifest); } /** * return the name of the resource * * @return Strig the name of the resource */ public String getResourceName() { return resourceName; } /** * Return the map of available extensions * * @return HashMap map of available extensions */ public HashMap getAvailableExtensions() { return availableExtensions; } /** * Return the list of required extensions * * @return ArrayList list of required extensions */ public ArrayList getRequiredExtensions() { return requiredExtensions; } // --------------------------------------------------------- Public Methods /** * Return the number of available extensions * * @return int the number of available extensions */ public int getAvailableExtensionCount() { if (availableExtensions == null) return 0; return availableExtensions.size(); } /** * Return the number of required extensions * * @return int the number of required extensions */ public int getRequiredExtensionCount() { if (requiredExtensions == null) return 0; return requiredExtensions.size(); } /** * Convienience method to check if this ManifestResource * has an requires extensions. * * @return true if required extensions are present */ public boolean requiresExtensions() { if (getRequiredExtensionCount() > 0) return true; else return false; } /** * Convienience method to check if this ManifestResource * has an extension available. * * @param key extension identifier * * @return true if extension available */ public boolean containsExtension(String key) { return (availableExtensions.containsKey(key)); } /** * Returns true if all required extension dependencies * have been meet for this ManifestResource object. * * @return boolean true if extension dependencies satisfied */ public boolean isFulfilled() { if ((requiredExtensions == null) || requiredExtensions.size() ==0) { return false; } Iterator it = requiredExtensions.iterator(); while (it.hasNext()) { Extension ext = (Extension)it.next(); if (!ext.isFulfilled()) return false; } return true; } public String toString() { StringBuffer sb = new StringBuffer("ManifestResource["); sb.append(resourceName); sb.append(", isFulfilled="); sb.append(isFulfilled() +""); sb.append(", requiredExtensionCount ="); sb.append(getRequiredExtensionCount()); sb.append(", availableExtensionCount="); sb.append(getAvailableExtensionCount()); switch (resourceType) { case SYSTEM : sb.append(", resourceType=SYSTEM"); break; case WAR : sb.append(", resourceType=WAR"); break; case APPLICATION : sb.append(", resourceType=APPLICATION"); break; } sb.append("]"); return (sb.toString()); } // -------------------------------------------------------- Private Methods private void processManifest(Manifest manifest) { availableExtensions = getAvailableExtensions(manifest); requiredExtensions = getRequiredExtensions(manifest); } /** * Return the set of Extension objects representing optional * packages that are required by the application contained in the JAR or WAR * file associated with the specified Manifest. If there * are no such optional packages, null is returned. * * @param manifest Manifest to be parsed * * @return ArrayList list of required extensions */ private ArrayList getRequiredExtensions(Manifest manifest) { ArrayList extensionList = new ArrayList(); Attributes attributes = manifest.getMainAttributes(); String names = attributes.getValue("Extension-List"); if (names == null) return null; names += " "; while (true) { int space = names.indexOf(' '); if (space < 0) break; String name = names.substring(0, space).trim(); names = names.substring(space + 1); String value = attributes.getValue(name + "-Extension-Name"); if (value == null) continue; Extension extension = new Extension(); extension.setExtensionName(value); extension.setImplementationURL (attributes.getValue(name + "-Implementation-URL")); extension.setImplementationVendorId (attributes.getValue(name + "-Implementation-Vendor-Id")); String version = attributes.getValue(name + "-Implementation-Version"); extension.setImplementationVersion(version); extension.setSpecificationVersion (attributes.getValue(name + "-Specification-Version")); extensionList.add(extension); } return extensionList; } /** * Return the set of Extension objects representing optional * packages that are avaiable by the application contained in the JAR * file associated with the specified Manifest. If there * are no such optional packages, a zero-length list is returned. * * @param manifest Manifest to be parsed * * @return HashMap Map of available extensions */ private HashMap getAvailableExtensions(Manifest manifest) { HashMap extensionMap = new HashMap(); Attributes attributes = manifest.getMainAttributes(); String name = attributes.getValue("Extension-Name"); if (name == null) return (null); Extension extension = new Extension(); extension.setExtensionName(name); extension.setImplementationURL (attributes.getValue("Implementation-URL")); extension.setImplementationVendor (attributes.getValue("Implementation-Vendor")); extension.setImplementationVendorId (attributes.getValue("Implementation-Vendor-Id")); String version = attributes.getValue("Implementation-Version"); extension.setImplementationVersion(version); extension.setSpecificationVersion (attributes.getValue("Specification-Version")); // creating a unique extension identifier with the key and value pair if (!extensionMap.containsKey(name + version)) extensionMap.put(name + version, extension); return extensionMap; } } -- To unsubscribe, e-mail: For additional commands, e-mail: