001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/Extension.java,v 1.4 2001/07/22 20:25:10 pier Exp $
003: * $Revision: 1.4 $
004: * $Date: 2001/07/22 20:25:10 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.loader;
065:
066: import java.util.ArrayList;
067: import java.util.Iterator;
068: import java.util.List;
069: import java.util.Map;
070: import java.util.StringTokenizer;
071: import java.util.jar.Attributes;
072: import java.util.jar.Manifest;
073:
074: /**
075: * Utility class that represents either an available "Optional Package"
076: * (formerly known as "Standard Extension") as described in the manifest
077: * of a JAR file, or the requirement for such an optional package. It is
078: * used to support the requirements of the Servlet Specification, version
079: * 2.3, related to providing shared extensions to all webapps.
080: * <p>
081: * In addition, static utility methods are available to scan a manifest
082: * and return an array of either available or required optional modules
083: * documented in that manifest.
084: * <p>
085: * For more information about optional packages, see the document
086: * <em>Optional Package Versioning</em> in the documentation bundle for your
087: * Java2 Standard Edition package, in file
088: * <code>guide/extensions/versioning.html</code>.
089: *
090: * @author Craig R. McClanahan
091: * @version $Revision: 1.4 $ $Date: 2001/07/22 20:25:10 $
092: */
093:
094: public final class Extension {
095:
096: // ------------------------------------------------------------- Properties
097:
098: /**
099: * The name of the optional package being made available, or required.
100: */
101: private String extensionName = null;
102:
103: public String getExtensionName() {
104: return (this .extensionName);
105: }
106:
107: public void setExtensionName(String extensionName) {
108: this .extensionName = extensionName;
109: }
110:
111: /**
112: * The URL from which the most recent version of this optional package
113: * can be obtained if it is not already installed.
114: */
115: private String implementationURL = null;
116:
117: public String getImplementationURL() {
118: return (this .implementationURL);
119: }
120:
121: public void setImplementationURL(String implementationURL) {
122: this .implementationURL = implementationURL;
123: }
124:
125: /**
126: * The name of the company or organization that produced this
127: * implementation of this optional package.
128: */
129: private String implementationVendor = null;
130:
131: public String getImplementationVendor() {
132: return (this .implementationVendor);
133: }
134:
135: public void setImplementationVendor(String implementationVendor) {
136: this .implementationVendor = implementationVendor;
137: }
138:
139: /**
140: * The unique identifier of the company that produced the optional
141: * package contained in this JAR file.
142: */
143: private String implementationVendorId = null;
144:
145: public String getImplementationVendorId() {
146: return (this .implementationVendorId);
147: }
148:
149: public void setImplementationVendorId(String implementationVendorId) {
150: this .implementationVendorId = implementationVendorId;
151: }
152:
153: /**
154: * The version number (dotted decimal notation) for this implementation
155: * of the optional package.
156: */
157: private String implementationVersion = null;
158:
159: public String getImplementationVersion() {
160: return (this .implementationVersion);
161: }
162:
163: public void setImplementationVersion(String implementationVersion) {
164: this .implementationVersion = implementationVersion;
165: }
166:
167: /**
168: * The name of the company or organization that originated the
169: * specification to which this optional package conforms.
170: */
171: private String specificationVendor = null;
172:
173: public String getSpecificationVendor() {
174: return (this .specificationVendor);
175: }
176:
177: public void setSpecificationVendor(String specificationVendor) {
178: this .specificationVendor = specificationVendor;
179: }
180:
181: /**
182: * The version number (dotted decimal notation) of the specification
183: * to which this optional package conforms.
184: */
185: private String specificationVersion = null;
186:
187: public String getSpecificationVersion() {
188: return (this .specificationVersion);
189: }
190:
191: public void setSpecificationVersion(String specificationVersion) {
192: this .specificationVersion = specificationVersion;
193: }
194:
195: // --------------------------------------------------------- Public Methods
196:
197: /**
198: * Return <code>true</code> if the specified <code>Extension</code>
199: * (which represents an optional package required by this application)
200: * is satisfied by this <code>Extension</code> (which represents an
201: * optional package that is already installed. Otherwise, return
202: * <code>false</code>.
203: *
204: * @param required Description of the required optional package
205: */
206: public boolean isCompatibleWith(Extension required) {
207:
208: // Extension Name must match
209: if (extensionName == null)
210: return (false);
211: if (!extensionName.equals(required.getExtensionName()))
212: return (false);
213:
214: // Available specification version must be >= required
215: if (!isNewer(specificationVersion, required
216: .getSpecificationVersion()))
217: return (false);
218:
219: // Implementation Vendor ID must match
220: if (implementationVendorId == null)
221: return (false);
222: if (!implementationVendorId.equals(required
223: .getImplementationVendorId()))
224: return (false);
225:
226: // Implementation version must be >= required
227: if (!isNewer(implementationVersion, required
228: .getImplementationVersion()))
229: return (false);
230:
231: // This available optional package satisfies the requirements
232: return (true);
233:
234: }
235:
236: /**
237: * Return a String representation of this object.
238: */
239: public String toString() {
240:
241: StringBuffer sb = new StringBuffer("Extension[");
242: sb.append(extensionName);
243: if (implementationURL != null) {
244: sb.append(", implementationURL=");
245: sb.append(implementationURL);
246: }
247: if (implementationVendor != null) {
248: sb.append(", implementationVendor=");
249: sb.append(implementationVendor);
250: }
251: if (implementationVendorId != null) {
252: sb.append(", implementationVendorId=");
253: sb.append(implementationVendorId);
254: }
255: if (implementationVersion != null) {
256: sb.append(", implementationVersion=");
257: sb.append(implementationVersion);
258: }
259: if (specificationVendor != null) {
260: sb.append(", specificationVendor=");
261: sb.append(specificationVendor);
262: }
263: if (specificationVersion != null) {
264: sb.append(", specificationVersion=");
265: sb.append(specificationVersion);
266: }
267: sb.append("]");
268: return (sb.toString());
269:
270: }
271:
272: // --------------------------------------------------------- Static Methods
273:
274: /**
275: * Return the set of <code>Extension</code> objects representing optional
276: * packages that are available in the JAR file associated with the
277: * specified <code>Manifest</code>. If there are no such optional
278: * packages, a zero-length list is returned.
279: *
280: * @param manifest Manifest to be parsed
281: */
282: public static List getAvailable(Manifest manifest) {
283:
284: ArrayList results = new ArrayList();
285: if (manifest == null)
286: return (results);
287: Extension extension = null;
288:
289: Attributes attributes = manifest.getMainAttributes();
290: if (attributes != null) {
291: extension = getAvailable(attributes);
292: if (extension != null)
293: results.add(extension);
294: }
295:
296: Map entries = manifest.getEntries();
297: Iterator keys = entries.keySet().iterator();
298: while (keys.hasNext()) {
299: String key = (String) keys.next();
300: attributes = (Attributes) entries.get(key);
301: extension = getAvailable(attributes);
302: if (extension != null)
303: results.add(extension);
304: }
305:
306: return (results);
307:
308: }
309:
310: /**
311: * Return the set of <code>Extension</code> objects representing optional
312: * packages that are required by the application contained in the JAR
313: * file associated with the specified <code>Manifest</code>. If there
314: * are no such optional packages, a zero-length list is returned.
315: *
316: * @param manifest Manifest to be parsed
317: */
318: public static List getRequired(Manifest manifest) {
319:
320: ArrayList results = new ArrayList();
321:
322: Attributes attributes = manifest.getMainAttributes();
323: if (attributes != null) {
324: Iterator required = getRequired(attributes).iterator();
325: while (required.hasNext())
326: results.add(required.next());
327: }
328:
329: Map entries = manifest.getEntries();
330: Iterator keys = entries.keySet().iterator();
331: while (keys.hasNext()) {
332: String key = (String) keys.next();
333: attributes = (Attributes) entries.get(key);
334: Iterator required = getRequired(attributes).iterator();
335: while (required.hasNext())
336: results.add(required.next());
337: }
338:
339: return (results);
340:
341: }
342:
343: // -------------------------------------------------------- Private Methods
344:
345: /**
346: * If the specified manifest attributes entry represents an available
347: * optional package, construct and return an <code>Extension</code>
348: * instance representing this package; otherwise return <code>null</code>.
349: *
350: * @param attributes Manifest attributes to be parsed
351: */
352: private static Extension getAvailable(Attributes attributes) {
353:
354: String name = attributes.getValue("Extension-Name");
355: if (name == null)
356: return (null);
357: Extension extension = new Extension();
358: extension.setExtensionName(name);
359:
360: extension.setImplementationVendor(attributes
361: .getValue("Implementation-Vendor"));
362: extension.setImplementationVendorId(attributes
363: .getValue("Implementation-Vendor-Id"));
364: extension.setImplementationVersion(attributes
365: .getValue("Implementation-Version"));
366: extension.setSpecificationVendor(attributes
367: .getValue("Specification-Vendor"));
368: extension.setSpecificationVersion(attributes
369: .getValue("Specification-Version"));
370:
371: return (extension);
372:
373: }
374:
375: /**
376: * Return the set of required optional packages defined in the specified
377: * attributes entry, if any. If no such optional packages are found,
378: * a zero-length list is returned.
379: *
380: * @param attributes Attributes to be parsed
381: */
382: private static List getRequired(Attributes attributes) {
383:
384: ArrayList results = new ArrayList();
385: String names = attributes.getValue("Extension-List");
386: if (names == null)
387: return (results);
388: names += " ";
389:
390: while (true) {
391:
392: int space = names.indexOf(' ');
393: if (space < 0)
394: break;
395: String name = names.substring(0, space).trim();
396: names = names.substring(space + 1);
397:
398: String value = attributes
399: .getValue(name + "-Extension-Name");
400: if (value == null)
401: continue;
402: Extension extension = new Extension();
403: extension.setExtensionName(value);
404:
405: extension.setImplementationURL(attributes.getValue(name
406: + "-Implementation-URL"));
407: extension.setImplementationVendorId(attributes
408: .getValue(name + "-Implementation-Vendor-Id"));
409: extension.setImplementationVersion(attributes.getValue(name
410: + "-Implementation-Version"));
411: extension.setSpecificationVersion(attributes.getValue(name
412: + "-Specification-Version"));
413:
414: results.add(extension);
415:
416: }
417:
418: return (results);
419:
420: }
421:
422: /**
423: * Return <code>true</code> if the first version number is greater than
424: * or equal to the second; otherwise return <code>false</code>.
425: *
426: * @param first First version number (dotted decimal)
427: * @param second Second version number (dotted decimal)
428: *
429: * @exception NumberFormatException on a malformed version number
430: */
431: private boolean isNewer(String first, String second)
432: throws NumberFormatException {
433:
434: if ((first == null) || (second == null))
435: return (false);
436: if (first.equals(second))
437: return (true);
438:
439: StringTokenizer fTok = new StringTokenizer(first, ".", true);
440: StringTokenizer sTok = new StringTokenizer(second, ".", true);
441: int fVersion = 0;
442: int sVersion = 0;
443: while (fTok.hasMoreTokens() || sTok.hasMoreTokens()) {
444: if (fTok.hasMoreTokens())
445: fVersion = Integer.parseInt(fTok.nextToken());
446: else
447: fVersion = 0;
448: if (sTok.hasMoreTokens())
449: sVersion = Integer.parseInt(sTok.nextToken());
450: else
451: sVersion = 0;
452: if (fVersion < sVersion)
453: return (false);
454: else if (fVersion > sVersion)
455: return (true);
456: if (fTok.hasMoreTokens()) // Swallow the periods
457: fTok.nextToken();
458: if (sTok.hasMoreTokens())
459: sTok.nextToken();
460: }
461:
462: return (true); // Exact match
463:
464: }
465:
466: }
|