View Javadoc

1   /*
2    * $Header: /cvsroot/dspro/dspro/src/java/com/explosion/utilities/classes/Extension.java,v 1.1 2004/05/31 10:55:16 uanda Exp $
3    * $Revision: 1.1 $
4    * $Date: 2004/05/31 10:55:16 $
5    *
6    * ====================================================================
7    *
8    * The Apache Software License, Version 1.1
9    *
10   * Copyright (c) 1999 The Apache Software Foundation.  All rights
11   * reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   *
17   * 1. Redistributions of source code must retain the above copyright
18   *    notice, this list of conditions and the following disclaimer.
19   *
20   * 2. Redistributions in binary form must reproduce the above copyright
21   *    notice, this list of conditions and the following disclaimer in
22   *    the documentation and/or other materials provided with the
23   *    distribution.
24   *
25   * 3. The end-user documentation included with the redistribution, if
26   *    any, must include the following acknowlegement:
27   *       "This product includes software developed by the
28   *        Apache Software Foundation (http://www.apache.org/)."
29   *    Alternately, this acknowlegement may appear in the software itself,
30   *    if and wherever such third-party acknowlegements normally appear.
31   *
32   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
33   *    Foundation" must not be used to endorse or promote products derived
34   *    from this software without prior written permission. For written
35   *    permission, please contact apache@apache.org.
36   *
37   * 5. Products derived from this software may not be called "Apache"
38   *    nor may "Apache" appear in their names without prior written
39   *    permission of the Apache Group.
40   *
41   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52   * SUCH DAMAGE.
53   * ====================================================================
54   *
55   * This software consists of voluntary contributions made by many
56   * individuals on behalf of the Apache Software Foundation.  For more
57   * information on the Apache Software Foundation, please see
58   * <http://www.apache.org/>.
59   *
60   * [Additional notices, if required by prior licensing conditions]
61   *
62   */
63  
64  
65  package com.explosion.utilities.classes;
66  
67  
68  import java.util.ArrayList;
69  import java.util.Iterator;
70  import java.util.List;
71  import java.util.Map;
72  import java.util.StringTokenizer;
73  import java.util.jar.Attributes;
74  import java.util.jar.Manifest;
75  
76  
77  /***
78   * Utility class that represents either an available "Optional Package"
79   * (formerly known as "Standard Extension") as described in the manifest
80   * of a JAR file, or the requirement for such an optional package.  It is
81   * used to support the requirements of the Servlet Specification, version
82   * 2.3, related to providing shared extensions to all webapps.
83   * <p>
84   * In addition, static utility methods are available to scan a manifest
85   * and return an array of either available or required optional modules
86   * documented in that manifest.
87   * <p>
88   * For more information about optional packages, see the document
89   * <em>Optional Package Versioning</em> in the documentation bundle for your
90   * Java2 Standard Edition package, in file
91   * <code>guide/extensions/versioning.html</code>.
92   *
93   * @author Craig R. McClanahan
94   * @version $Revision: 1.1 $ $Date: 2004/05/31 10:55:16 $
95   */
96  
97  public final class Extension {
98  
99  
100     // ------------------------------------------------------------- Properties
101 
102 
103     /***
104      * The name of the optional package being made available, or required.
105      */
106     private String extensionName = null;
107 
108     public String getExtensionName() {
109         return (this.extensionName);
110     }
111 
112     public void setExtensionName(String extensionName) {
113         this.extensionName = extensionName;
114     }
115 
116 
117     /***
118      * The URL from which the most recent version of this optional package
119      * can be obtained if it is not already installed.
120      */
121     private String implementationURL = null;
122 
123     public String getImplementationURL() {
124         return (this.implementationURL);
125     }
126 
127     public void setImplementationURL(String implementationURL) {
128         this.implementationURL = implementationURL;
129     }
130 
131 
132     /***
133      * The name of the company or organization that produced this
134      * implementation of this optional package.
135      */
136     private String implementationVendor = null;
137 
138     public String getImplementationVendor() {
139         return (this.implementationVendor);
140     }
141 
142     public void setImplementationVendor(String implementationVendor) {
143         this.implementationVendor = implementationVendor;
144     }
145 
146 
147     /***
148      * The unique identifier of the company that produced the optional
149      * package contained in this JAR file.
150      */
151     private String implementationVendorId = null;
152 
153     public String getImplementationVendorId() {
154         return (this.implementationVendorId);
155     }
156 
157     public void setImplementationVendorId(String implementationVendorId) {
158         this.implementationVendorId = implementationVendorId;
159     }
160 
161 
162     /***
163      * The version number (dotted decimal notation) for this implementation
164      * of the optional package.
165      */
166     private String implementationVersion = null;
167 
168     public String getImplementationVersion() {
169         return (this.implementationVersion);
170     }
171 
172     public void setImplementationVersion(String implementationVersion) {
173         this.implementationVersion = implementationVersion;
174     }
175 
176 
177     /***
178      * The name of the company or organization that originated the
179      * specification to which this optional package conforms.
180      */
181     private String specificationVendor = null;
182 
183     public String getSpecificationVendor() {
184         return (this.specificationVendor);
185     }
186 
187     public void setSpecificationVendor(String specificationVendor) {
188         this.specificationVendor = specificationVendor;
189     }
190 
191 
192     /***
193      * The version number (dotted decimal notation) of the specification
194      * to which this optional package conforms.
195      */
196     private String specificationVersion = null;
197 
198     public String getSpecificationVersion() {
199         return (this.specificationVersion);
200     }
201 
202     public void setSpecificationVersion(String specificationVersion) {
203         this.specificationVersion = specificationVersion;
204     }
205 
206 
207     // --------------------------------------------------------- Public Methods
208 
209 
210     /***
211      * Return <code>true</code> if the specified <code>Extension</code>
212      * (which represents an optional package required by this application)
213      * is satisfied by this <code>Extension</code> (which represents an
214      * optional package that is already installed.  Otherwise, return
215      * <code>false</code>.
216      *
217      * @param required Description of the required optional package
218      */
219     public boolean isCompatibleWith(Extension required) {
220 
221         // Extension Name must match
222         if (extensionName == null)
223             return (false);
224         if (!extensionName.equals(required.getExtensionName()))
225             return (false);
226 
227         // Available specification version must be >= required
228         if (!isNewer(specificationVersion, required.getSpecificationVersion()))
229             return (false);
230 
231         // Implementation Vendor ID must match
232         if (implementationVendorId == null)
233             return (false);
234         if (!implementationVendorId.equals(required.getImplementationVendorId()))
235             return (false);
236 
237         // Implementation version must be >= required
238         if (!isNewer(implementationVersion, required.getImplementationVersion()))
239             return (false);
240 
241         // This available optional package satisfies the requirements
242         return (true);
243 
244     }
245 
246 
247     /***
248      * Return a String representation of this object.
249      */
250     public String toString() {
251 
252         StringBuffer sb = new StringBuffer("Extension[");
253         sb.append(extensionName);
254         if (implementationURL != null) {
255             sb.append(", implementationURL=");
256             sb.append(implementationURL);
257         }
258         if (implementationVendor != null) {
259             sb.append(", implementationVendor=");
260             sb.append(implementationVendor);
261         }
262         if (implementationVendorId != null) {
263             sb.append(", implementationVendorId=");
264             sb.append(implementationVendorId);
265         }
266         if (implementationVersion != null) {
267             sb.append(", implementationVersion=");
268             sb.append(implementationVersion);
269         }
270         if (specificationVendor != null) {
271             sb.append(", specificationVendor=");
272             sb.append(specificationVendor);
273         }
274         if (specificationVersion != null) {
275             sb.append(", specificationVersion=");
276             sb.append(specificationVersion);
277         }
278         sb.append("]");
279         return (sb.toString());
280 
281     }
282 
283 
284     // --------------------------------------------------------- Static Methods
285 
286 
287     /***
288      * Return the set of <code>Extension</code> objects representing optional
289      * packages that are available in the JAR file associated with the
290      * specified <code>Manifest</code>.  If there are no such optional
291      * packages, a zero-length list is returned.
292      *
293      * @param manifest Manifest to be parsed
294      */
295     public static List getAvailable(Manifest manifest) {
296 
297         ArrayList results = new ArrayList();
298         if (manifest == null)
299             return (results);
300         Extension extension = null;
301 
302         Attributes attributes = manifest.getMainAttributes();
303         if (attributes != null) {
304             extension = getAvailable(attributes);
305             if (extension != null)
306                 results.add(extension);
307         }
308 
309         Map entries = manifest.getEntries();
310         Iterator keys = entries.keySet().iterator();
311         while (keys.hasNext()) {
312             String key = (String) keys.next();
313             attributes = (Attributes) entries.get(key);
314             extension = getAvailable(attributes);
315             if (extension != null)
316                 results.add(extension);
317         }
318 
319         return (results);
320 
321     }
322 
323 
324     /***
325      * Return the set of <code>Extension</code> objects representing optional
326      * packages that are required by the application contained in the JAR
327      * file associated with the specified <code>Manifest</code>.  If there
328      * are no such optional packages, a zero-length list is returned.
329      *
330      * @param manifest Manifest to be parsed
331      */
332     public static List getRequired(Manifest manifest) {
333 
334         ArrayList results = new ArrayList();
335 
336         Attributes attributes = manifest.getMainAttributes();
337         if (attributes != null) {
338             Iterator required = getRequired(attributes).iterator();
339             while (required.hasNext())
340                 results.add(required.next());
341         }
342 
343         Map entries = manifest.getEntries();
344         Iterator keys = entries.keySet().iterator();
345         while (keys.hasNext()) {
346             String key = (String) keys.next();
347             attributes = (Attributes) entries.get(key);
348             Iterator required = getRequired(attributes).iterator();
349             while (required.hasNext())
350                 results.add(required.next());
351         }
352 
353         return (results);
354 
355     }
356 
357 
358     // -------------------------------------------------------- Private Methods
359 
360 
361     /***
362      * If the specified manifest attributes entry represents an available
363      * optional package, construct and return an <code>Extension</code>
364      * instance representing this package; otherwise return <code>null</code>.
365      *
366      * @param attributes Manifest attributes to be parsed
367      */
368     private static Extension getAvailable(Attributes attributes) {
369 
370         String name = attributes.getValue("Extension-Name");
371         if (name == null)
372             return (null);
373         Extension extension = new Extension();
374         extension.setExtensionName(name);
375 
376         extension.setImplementationVendor
377             (attributes.getValue("Implementation-Vendor"));
378         extension.setImplementationVendorId
379             (attributes.getValue("Implementation-Vendor-Id"));
380         extension.setImplementationVersion
381             (attributes.getValue("Implementation-Version"));
382         extension.setSpecificationVendor
383             (attributes.getValue("Specification-Vendor"));
384         extension.setSpecificationVersion
385             (attributes.getValue("Specification-Version"));
386 
387         return (extension);
388 
389     }
390 
391 
392     /***
393      * Return the set of required optional packages defined in the specified
394      * attributes entry, if any.  If no such optional packages are found,
395      * a zero-length list is returned.
396      *
397      * @param attributes Attributes to be parsed
398      */
399     private static List getRequired(Attributes attributes) {
400 
401         ArrayList results = new ArrayList();
402         String names = attributes.getValue("Extension-List");
403         if (names == null)
404             return (results);
405         names += " ";
406 
407         while (true) {
408 
409             int space = names.indexOf(' ');
410             if (space < 0)
411                 break;
412             String name = names.substring(0, space).trim();
413             names = names.substring(space + 1);
414 
415             String value =
416                 attributes.getValue(name + "-Extension-Name");
417             if (value == null)
418                 continue;
419             Extension extension = new Extension();
420             extension.setExtensionName(value);
421 
422             extension.setImplementationURL
423                 (attributes.getValue(name + "-Implementation-URL"));
424             extension.setImplementationVendorId
425                 (attributes.getValue(name + "-Implementation-Vendor-Id"));
426             extension.setImplementationVersion
427                 (attributes.getValue(name + "-Implementation-Version"));
428             extension.setSpecificationVersion
429                 (attributes.getValue(name + "-Specification-Version"));
430 
431             results.add(extension);
432 
433         }
434 
435         return (results);
436 
437     }
438 
439 
440     /***
441      * Return <code>true</code> if the first version number is greater than
442      * or equal to the second; otherwise return <code>false</code>.
443      *
444      * @param first First version number (dotted decimal)
445      * @param second Second version number (dotted decimal)
446      *
447      * @exception NumberFormatException on a malformed version number
448      */
449     private boolean isNewer(String first, String second)
450         throws NumberFormatException {
451 
452         if ((first == null) || (second == null))
453             return (false);
454         if (first.equals(second))
455             return (true);
456 
457         StringTokenizer fTok = new StringTokenizer(first, ".", true);
458         StringTokenizer sTok = new StringTokenizer(second, ".", true);
459         int fVersion = 0;
460         int sVersion = 0;
461         while (fTok.hasMoreTokens() || sTok.hasMoreTokens()) {
462             if (fTok.hasMoreTokens())
463                 fVersion = Integer.parseInt(fTok.nextToken());
464             else
465                 fVersion = 0;
466             if (sTok.hasMoreTokens())
467                 sVersion = Integer.parseInt(sTok.nextToken());
468             else
469                 sVersion = 0;
470             if (fVersion < sVersion)
471                 return (false);
472             else if (fVersion > sVersion)
473                 return (true);
474             if (fTok.hasMoreTokens())   // Swallow the periods
475                 fTok.nextToken();
476             if (sTok.hasMoreTokens())
477                 sTok.nextToken();
478         }
479 
480         return (true);  // Exact match
481 
482     }
483 
484 
485 }