001: /*
002: * @(#)ExtensionDependency.java 1.14 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.misc;
029:
030: import java.io.File;
031: import java.io.IOException;
032: import java.io.FileNotFoundException;
033: import java.util.StringTokenizer;
034: import java.util.Vector;
035: import java.util.Enumeration;
036: import java.util.jar.JarFile;
037: import java.util.jar.Manifest;
038: import java.util.jar.Attributes;
039: import java.util.jar.Attributes.Name;
040: import java.security.AccessController;
041: import java.security.PrivilegedAction;
042: import java.security.PrivilegedExceptionAction;
043: import java.security.PrivilegedActionException;
044: import java.net.URL;
045: import java.net.MalformedURLException;
046:
047: /**
048: * <p>
049: * This class checks dependent extensions a particular jar file may have
050: * declared through its manifest attributes.
051: * <p>
052: * Jar file declared dependent extensions through the extension-list
053: * attribute. The extension-list contains a list of keys used to
054: * fetch the other attributes describing the required extension.
055: * If key is the extension key declared in the extension-list
056: * attribute, the following describing attribute can be found in
057: * the manifest :
058: * key-Extension-Name: (Specification package name)
059: * key-Specification-Version: (Specification-Version)
060: * key-Implementation-Version: (Implementation-Version)
061: * key-Implementation-Vendor-Id: (Imlementation-Vendor-Id)
062: * key-Implementation-Version: (Implementation version)
063: * key-Implementation-URL: (URL to download the requested extension)
064: * <p>
065: * This class also maintain versioning consistency of installed
066: * extensions dependencies declared in jar file manifest.
067: *
068: * @author Jerome Dochez
069: * @version 1.5, 09/28/99
070: */
071: public class ExtensionDependency {
072:
073: /* Callbak interfaces to delegate installation of missing extensions */
074: private static Vector providers;
075:
076: /**
077: * <p>
078: * Register an ExtensionInstallationProvider. The provider is responsible
079: * for handling the installation (upgrade) of any missing extensions.
080: * </p>
081: * @param eip ExtensionInstallationProvider implementation
082: */
083: public synchronized static void addExtensionInstallationProvider(
084: ExtensionInstallationProvider eip) {
085: if (providers == null) {
086: providers = new Vector();
087: }
088: providers.add(eip);
089: }
090:
091: /**
092: * <p>
093: * Unregister a previously installed installation provider
094: * </p>
095: */
096: public synchronized static void removeExtensionInstallationProvider(
097: ExtensionInstallationProvider eip) {
098: providers.remove(eip);
099: }
100:
101: /**
102: * <p>
103: * Checks the dependencies of the jar file on installed extension.
104: * </p>
105: * @param jarFile containing the attriutes declaring the dependencies
106: */
107: public static synchronized boolean checkExtensionsDependencies(
108: JarFile jar) {
109: if (providers == null) {
110: // no need to bother, nobody is registered to install missing
111: // extensions
112: return true;
113: }
114:
115: try {
116: ExtensionDependency extDep = new ExtensionDependency();
117: return extDep.checkExtensions(jar);
118: } catch (ExtensionInstallationException e) {
119: debug(e.getMessage());
120: }
121: return false;
122: }
123:
124: /*
125: * Check for all declared required extensions in the jar file
126: * manifest.
127: */
128: protected boolean checkExtensions(JarFile jar)
129: throws ExtensionInstallationException {
130: Manifest man;
131: try {
132: man = jar.getManifest();
133: } catch (IOException e) {
134: return false;
135: }
136:
137: if (man == null) {
138: // The applet does not define a manifest file, so
139: // we just assume all dependencies are satisfied.
140: return true;
141: }
142:
143: boolean result = true;
144: Attributes attr = man.getMainAttributes();
145: if (attr != null) {
146: // Let's get the list of declared dependencies
147: String value = attr.getValue(Name.EXTENSION_LIST);
148: if (value != null) {
149: StringTokenizer st = new StringTokenizer(value);
150: // Iterate over all declared dependencies
151: while (st.hasMoreTokens()) {
152: String extensionName = st.nextToken();
153: debug("The file " + jar.getName()
154: + " appears to depend on " + extensionName);
155: // Sanity Check
156: String extName = extensionName + "-"
157: + Name.EXTENSION_NAME.toString();
158: if (attr.getValue(extName) == null) {
159: debug("The jar file " + jar.getName()
160: + " appers to depend on "
161: + extensionName
162: + " but does not define the " + extName
163: + " attribute in its manifest ");
164:
165: } else {
166: if (!checkExtension(extensionName, attr)) {
167: debug("Failed installing " + extensionName);
168: result = false;
169: }
170: }
171: }
172: } else {
173: debug("No dependencies for " + jar.getName());
174: }
175: }
176: return result;
177: }
178:
179: /*
180: * <p>
181: * Check that a particular dependency on an extension is satisfied.
182: * </p>
183: * @param extensionName is the key used for the attributes in the manifest
184: * @param attr is the attributes of the manifest file
185: *
186: * @return true if the dependency is satisfied by the installed extensions
187: */
188: protected boolean checkExtension(final String extensionName,
189: final Attributes attr)
190: throws ExtensionInstallationException {
191: debug("Checking extension " + extensionName);
192: if (checkExtensionAgainstInstalled(extensionName, attr))
193: return true;
194:
195: debug("Extension not currently installed ");
196: ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
197: return installExtension(reqInfo, null);
198: }
199:
200: /*
201: * <p>
202: * Check if a particular extension is part of the currently installed
203: * extensions.
204: * </p>
205: * @param extensionName is the key for the attributes in the manifest
206: * @param attr is the attributes of the manifest
207: *
208: * @return true if the requested extension is already installed
209: */
210: boolean checkExtensionAgainstInstalled(String extensionName,
211: Attributes attr) throws ExtensionInstallationException {
212:
213: // Get the list of installed extension jar file
214: // so we can compare the installed versus the requested extension
215: File[] installedExts;
216: try {
217: installedExts = getInstalledExtensions();
218: } catch (IOException e) {
219: debugException(e);
220: return false;
221: }
222: for (int i = 0; i < installedExts.length; i++) {
223: try {
224: if (checkExtensionAgainst(extensionName, attr,
225: installedExts[i]))
226: return true;
227: } catch (FileNotFoundException e) {
228: debugException(e);
229: } catch (IOException e) {
230: debugException(e);
231: // let's continue with the next installed extension
232: }
233: }
234: return false;
235: }
236:
237: /*
238: * <p>
239: * Check if the requested extension described by the attributes
240: * in the manifest under the key extensionName is compatible with
241: * the jar file.
242: * </p>
243: *
244: * @param extensionName key in the attibute list
245: * @param attr manifest file attributes
246: * @param file installed extension jar file to compare the requested
247: * extension against.
248: */
249: protected boolean checkExtensionAgainst(String extensionName,
250: Attributes attr, final File file) throws IOException,
251: FileNotFoundException, ExtensionInstallationException {
252:
253: debug("Checking extension " + extensionName + " against "
254: + file.getName());
255:
256: // Load the jar file ...
257: Manifest man;
258: try {
259: man = (Manifest) AccessController
260: .doPrivileged(new PrivilegedExceptionAction() {
261: public Object run() throws IOException,
262: FileNotFoundException {
263: if (!file.exists())
264: throw new FileNotFoundException(file
265: .getName());
266: JarFile jarFile = new JarFile(file);
267: return jarFile.getManifest();
268: }
269: });
270: } catch (PrivilegedActionException e) {
271: if (e.getException() instanceof FileNotFoundException)
272: throw (FileNotFoundException) e.getException();
273: throw (IOException) e.getException();
274: }
275:
276: // Construct the extension information object
277: ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
278: debug("Requested Extension : " + reqInfo);
279:
280: int isCompatible = ExtensionInfo.INCOMPATIBLE;
281: ExtensionInfo instInfo = null;
282:
283: if (man != null) {
284: Attributes instAttr = man.getMainAttributes();
285: if (instAttr != null) {
286: instInfo = new ExtensionInfo(null, instAttr);
287: debug("Extension Installed " + instInfo);
288: isCompatible = instInfo.isCompatibleWith(reqInfo);
289: switch (isCompatible) {
290: case ExtensionInfo.COMPATIBLE:
291: debug("Extensions are compatible");
292: return true;
293:
294: case ExtensionInfo.INCOMPATIBLE:
295: debug("Extensions are incompatible");
296: return false;
297:
298: default:
299: // everything else
300: debug("Extensions require an upgrade or vendor switch");
301: return installExtension(reqInfo, instInfo);
302:
303: }
304: }
305: }
306: return true;
307: }
308:
309: /*
310: * <p>
311: * An required extension is missing, if an ExtensionInstallationProvider is
312: * registered, delegate the installation of that particular extension to it.
313: * <p>
314: *
315: * @param reqInfo Missing extension information
316: * @param instInfo Older installed version information
317: *
318: * @return true if the installation is successful
319: */
320: protected boolean installExtension(ExtensionInfo reqInfo,
321: ExtensionInfo instInfo)
322: throws ExtensionInstallationException {
323:
324: Vector currentProviders;
325: synchronized (providers) {
326: currentProviders = (Vector) providers.clone();
327: }
328: for (Enumeration e = currentProviders.elements(); e
329: .hasMoreElements();) {
330: ExtensionInstallationProvider eip = (ExtensionInstallationProvider) e
331: .nextElement();
332:
333: if (eip != null) {
334: // delegate the installation to the provider
335: if (eip.installExtension(reqInfo, instInfo)) {
336: System.out.println("Installation successful");
337: Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader) Launcher
338: .getLauncher().getClassLoader().getParent();
339: addNewExtensionsToClassLoader(cl);
340: return true;
341: }
342: }
343: }
344: // We have tried all of our providers, noone could install this
345: // extension, we just return failure at this point
346: return false;
347: }
348:
349: /**
350: * <p>
351: * @return the java.ext.dirs property as a list of directory
352: * /p>
353: */
354: private static File[] getExtDirs() {
355: String s = System.getProperty("java.ext.dirs");
356: File[] dirs;
357: if (s != null) {
358: StringTokenizer st = new StringTokenizer(s,
359: File.pathSeparator);
360: int count = st.countTokens();
361: dirs = new File[count];
362: for (int i = 0; i < count; i++) {
363: dirs[i] = new File(st.nextToken());
364: }
365: } else {
366: dirs = new File[0];
367: }
368: return dirs;
369: }
370:
371: /*
372: * <p>
373: * Scan the directories and return all files installed in those
374: * </p>
375: * @param dirs list of directories to scan
376: *
377: * @return the list of files installed in all the directories
378: */
379: private static File[] getExtFiles(File[] dirs) throws IOException {
380: Vector urls = new Vector();
381: for (int i = 0; i < dirs.length; i++) {
382: String[] files = dirs[i].list();
383: if (files != null) {
384: for (int j = 0; j < files.length; j++) {
385: File f = new File(dirs[i], files[j]);
386: urls.add(f);
387: }
388: }
389: }
390: File[] ua = new File[urls.size()];
391: urls.copyInto(ua);
392: return ua;
393: }
394:
395: /*
396: * <p>
397: * @return the list of installed extensions jar files
398: * </p>
399: */
400: private File[] getInstalledExtensions() throws IOException {
401: return (File[]) AccessController
402: .doPrivileged(new PrivilegedAction() {
403: public Object run() {
404: try {
405: return getExtFiles(getExtDirs());
406: } catch (IOException e) {
407: debug("Cannot get list of installed extensions");
408: debugException(e);
409: return new URL[0];
410: }
411: }
412: });
413: }
414:
415: /*
416: * <p>
417: * Add the newly installed jar file to the extension class loader.
418: * </p>
419: *
420: * @param cl the current installed extension class loader
421: *
422: * @return true if successful
423: */
424: private Boolean addNewExtensionsToClassLoader(
425: Launcher.ExtClassLoader cl) {
426: // We need to check if the class loader is not null first.
427: // If java.ext.dirs is empty, we don't create a ExtClassLoader, and
428: // the AppClassLoader's parent is null. ExtClassLoader
429: // will only be created if java.ext.dirs is set to
430: // be non-empty.
431: if (cl == null) {
432: return Boolean.TRUE;
433: }
434:
435: try {
436: File[] installedExts = getInstalledExtensions();
437: for (int i = 0; i < installedExts.length; i++) {
438: final File instFile = installedExts[i];
439: URL instURL = (URL) AccessController
440: .doPrivileged(new PrivilegedAction() {
441: public Object run() {
442: try {
443: return (URL) instFile.toURL();
444: } catch (MalformedURLException e) {
445: debugException(e);
446: return null;
447: }
448: }
449: });
450: if (instURL != null) {
451: URL[] urls = cl.getURLs();
452: boolean found = false;
453: for (int j = 0; j < urls.length; j++) {
454: debug("URL[" + j + "] is " + urls[j]
455: + " looking for " + instURL);
456: if (urls[j].toString().compareToIgnoreCase(
457: instURL.toString()) == 0) {
458: found = true;
459: debug("Found !");
460: }
461: }
462: if (!found) {
463: debug("Not Found ! adding to the classloader "
464: + instURL);
465: cl.addExtURL(instURL);
466: }
467: }
468: }
469: } catch (MalformedURLException e) {
470: e.printStackTrace();
471: } catch (IOException e) {
472: e.printStackTrace();
473: // let's continue with the next installed extension
474: }
475: return Boolean.TRUE;
476: }
477:
478: // True to display all debug and trace messages
479: static final boolean DEBUG = false;
480:
481: private static void debug(String s) {
482: if (DEBUG) {
483: System.err.println(s);
484: }
485: }
486:
487: private void debugException(Throwable e) {
488: if (DEBUG) {
489: e.printStackTrace();
490: }
491: }
492:
493: }
|