1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
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
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
222 if (extensionName == null)
223 return (false);
224 if (!extensionName.equals(required.getExtensionName()))
225 return (false);
226
227
228 if (!isNewer(specificationVersion, required.getSpecificationVersion()))
229 return (false);
230
231
232 if (implementationVendorId == null)
233 return (false);
234 if (!implementationVendorId.equals(required.getImplementationVendorId()))
235 return (false);
236
237
238 if (!isNewer(implementationVersion, required.getImplementationVersion()))
239 return (false);
240
241
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
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
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())
475 fTok.nextToken();
476 if (sTok.hasMoreTokens())
477 sTok.nextToken();
478 }
479
480 return (true);
481
482 }
483
484
485 }