571 lines
20 KiB
Java
571 lines
20 KiB
Java
/*
|
|
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.misc;
|
|
|
|
import java.io.File;
|
|
import java.io.FilenameFilter;
|
|
import java.io.IOException;
|
|
import java.io.FileNotFoundException;
|
|
import java.util.StringTokenizer;
|
|
import java.util.Vector;
|
|
import java.util.Enumeration;
|
|
import java.util.jar.JarFile;
|
|
import java.util.jar.Manifest;
|
|
import java.util.jar.Attributes;
|
|
import java.util.jar.Attributes.Name;
|
|
import java.security.AccessController;
|
|
import java.security.PrivilegedAction;
|
|
import java.security.PrivilegedExceptionAction;
|
|
import java.security.PrivilegedActionException;
|
|
import java.net.URL;
|
|
import java.net.MalformedURLException;
|
|
import sun.net.www.ParseUtil;
|
|
|
|
/**
|
|
* <p>
|
|
* This class checks dependent extensions a particular jar file may have
|
|
* declared through its manifest attributes.
|
|
* </p>
|
|
* Jar file declared dependent extensions through the extension-list
|
|
* attribute. The extension-list contains a list of keys used to
|
|
* fetch the other attributes describing the required extension.
|
|
* If key is the extension key declared in the extension-list
|
|
* attribute, the following describing attribute can be found in
|
|
* the manifest :
|
|
* key-Extension-Name: (Specification package name)
|
|
* key-Specification-Version: (Specification-Version)
|
|
* key-Implementation-Version: (Implementation-Version)
|
|
* key-Implementation-Vendor-Id: (Imlementation-Vendor-Id)
|
|
* key-Implementation-Version: (Implementation version)
|
|
* key-Implementation-URL: (URL to download the requested extension)
|
|
* <p>
|
|
* This class also maintain versioning consistency of installed
|
|
* extensions dependencies declared in jar file manifest.
|
|
* </p>
|
|
* @author Jerome Dochez
|
|
*/
|
|
public class ExtensionDependency {
|
|
|
|
/* Callbak interfaces to delegate installation of missing extensions */
|
|
private static Vector<ExtensionInstallationProvider> providers;
|
|
|
|
/**
|
|
* <p>
|
|
* Register an ExtensionInstallationProvider. The provider is responsible
|
|
* for handling the installation (upgrade) of any missing extensions.
|
|
* </p>
|
|
* @param eip ExtensionInstallationProvider implementation
|
|
*/
|
|
public synchronized static void addExtensionInstallationProvider
|
|
(ExtensionInstallationProvider eip)
|
|
{
|
|
if (providers == null) {
|
|
providers = new Vector<>();
|
|
}
|
|
providers.add(eip);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Unregister a previously installed installation provider
|
|
* </p>
|
|
*/
|
|
public synchronized static void removeExtensionInstallationProvider
|
|
(ExtensionInstallationProvider eip)
|
|
{
|
|
providers.remove(eip);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Checks the dependencies of the jar file on installed extension.
|
|
* </p>
|
|
* @param jarFile containing the attriutes declaring the dependencies
|
|
*/
|
|
public static boolean checkExtensionsDependencies(JarFile jar)
|
|
{
|
|
if (providers == null) {
|
|
// no need to bother, nobody is registered to install missing
|
|
// extensions
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
ExtensionDependency extDep = new ExtensionDependency();
|
|
return extDep.checkExtensions(jar);
|
|
} catch (ExtensionInstallationException e) {
|
|
debug(e.getMessage());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Check for all declared required extensions in the jar file
|
|
* manifest.
|
|
*/
|
|
protected boolean checkExtensions(JarFile jar)
|
|
throws ExtensionInstallationException
|
|
{
|
|
Manifest man;
|
|
try {
|
|
man = jar.getManifest();
|
|
} catch (IOException e) {
|
|
return false;
|
|
}
|
|
|
|
if (man == null) {
|
|
// The applet does not define a manifest file, so
|
|
// we just assume all dependencies are satisfied.
|
|
return true;
|
|
}
|
|
|
|
boolean result = true;
|
|
Attributes attr = man.getMainAttributes();
|
|
if (attr != null) {
|
|
// Let's get the list of declared dependencies
|
|
String value = attr.getValue(Name.EXTENSION_LIST);
|
|
if (value != null) {
|
|
StringTokenizer st = new StringTokenizer(value);
|
|
// Iterate over all declared dependencies
|
|
while (st.hasMoreTokens()) {
|
|
String extensionName = st.nextToken();
|
|
debug("The file " + jar.getName() +
|
|
" appears to depend on " + extensionName);
|
|
// Sanity Check
|
|
String extName = extensionName + "-" +
|
|
Name.EXTENSION_NAME.toString();
|
|
if (attr.getValue(extName) == null) {
|
|
debug("The jar file " + jar.getName() +
|
|
" appers to depend on "
|
|
+ extensionName + " but does not define the " +
|
|
extName + " attribute in its manifest ");
|
|
|
|
} else {
|
|
if (!checkExtension(extensionName, attr)) {
|
|
debug("Failed installing " + extensionName);
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
debug("No dependencies for " + jar.getName());
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* <p>
|
|
* Check that a particular dependency on an extension is satisfied.
|
|
* </p>
|
|
* @param extensionName is the key used for the attributes in the manifest
|
|
* @param attr is the attributes of the manifest file
|
|
*
|
|
* @return true if the dependency is satisfied by the installed extensions
|
|
*/
|
|
protected synchronized boolean checkExtension(final String extensionName,
|
|
final Attributes attr)
|
|
throws ExtensionInstallationException
|
|
{
|
|
debug("Checking extension " + extensionName);
|
|
if (checkExtensionAgainstInstalled(extensionName, attr))
|
|
return true;
|
|
|
|
debug("Extension not currently installed ");
|
|
ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
|
|
return installExtension(reqInfo, null);
|
|
}
|
|
|
|
/*
|
|
* <p>
|
|
* Check if a particular extension is part of the currently installed
|
|
* extensions.
|
|
* </p>
|
|
* @param extensionName is the key for the attributes in the manifest
|
|
* @param attr is the attributes of the manifest
|
|
*
|
|
* @return true if the requested extension is already installed
|
|
*/
|
|
boolean checkExtensionAgainstInstalled(String extensionName,
|
|
Attributes attr)
|
|
throws ExtensionInstallationException
|
|
{
|
|
File fExtension = checkExtensionExists(extensionName);
|
|
|
|
if (fExtension != null) {
|
|
// Extension already installed, just check against this one
|
|
try {
|
|
if (checkExtensionAgainst(extensionName, attr, fExtension))
|
|
return true;
|
|
} catch (FileNotFoundException e) {
|
|
debugException(e);
|
|
} catch (IOException e) {
|
|
debugException(e);
|
|
}
|
|
return false;
|
|
|
|
} else {
|
|
// Not sure if extension is already installed, so check all the
|
|
// installed extension jar files to see if we get a match
|
|
|
|
File[] installedExts;
|
|
|
|
try {
|
|
// Get the list of installed extension jar files so we can
|
|
// compare the installed versus the requested extension
|
|
installedExts = getInstalledExtensions();
|
|
} catch(IOException e) {
|
|
debugException(e);
|
|
return false;
|
|
}
|
|
|
|
for (int i=0;i<installedExts.length;i++) {
|
|
try {
|
|
if (checkExtensionAgainst(extensionName, attr, installedExts[i]))
|
|
return true;
|
|
} catch (FileNotFoundException e) {
|
|
debugException(e);
|
|
} catch (IOException e) {
|
|
debugException(e);
|
|
// let's continue with the next installed extension
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* <p>
|
|
* Check if the requested extension described by the attributes
|
|
* in the manifest under the key extensionName is compatible with
|
|
* the jar file.
|
|
* </p>
|
|
*
|
|
* @param extensionName key in the attribute list
|
|
* @param attr manifest file attributes
|
|
* @param file installed extension jar file to compare the requested
|
|
* extension against.
|
|
*/
|
|
protected boolean checkExtensionAgainst(String extensionName,
|
|
Attributes attr,
|
|
final File file)
|
|
throws IOException,
|
|
FileNotFoundException,
|
|
ExtensionInstallationException
|
|
{
|
|
|
|
debug("Checking extension " + extensionName +
|
|
" against " + file.getName());
|
|
|
|
// Load the jar file ...
|
|
Manifest man;
|
|
try {
|
|
man = AccessController.doPrivileged(
|
|
new PrivilegedExceptionAction<Manifest>() {
|
|
public Manifest run()
|
|
throws IOException, FileNotFoundException {
|
|
if (!file.exists())
|
|
throw new FileNotFoundException(file.getName());
|
|
JarFile jarFile = new JarFile(file);
|
|
return jarFile.getManifest();
|
|
}
|
|
});
|
|
} catch(PrivilegedActionException e) {
|
|
if (e.getException() instanceof FileNotFoundException)
|
|
throw (FileNotFoundException) e.getException();
|
|
throw (IOException) e.getException();
|
|
}
|
|
|
|
// Construct the extension information object
|
|
ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
|
|
debug("Requested Extension : " + reqInfo);
|
|
|
|
int isCompatible = ExtensionInfo.INCOMPATIBLE;
|
|
ExtensionInfo instInfo = null;
|
|
|
|
if (man != null) {
|
|
Attributes instAttr = man.getMainAttributes();
|
|
if (instAttr != null) {
|
|
instInfo = new ExtensionInfo(null, instAttr);
|
|
debug("Extension Installed " + instInfo);
|
|
isCompatible = instInfo.isCompatibleWith(reqInfo);
|
|
switch(isCompatible) {
|
|
case ExtensionInfo.COMPATIBLE:
|
|
debug("Extensions are compatible");
|
|
return true;
|
|
|
|
case ExtensionInfo.INCOMPATIBLE:
|
|
debug("Extensions are incompatible");
|
|
return false;
|
|
|
|
default:
|
|
// everything else
|
|
debug("Extensions require an upgrade or vendor switch");
|
|
return installExtension(reqInfo, instInfo);
|
|
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* <p>
|
|
* An required extension is missing, if an ExtensionInstallationProvider is
|
|
* registered, delegate the installation of that particular extension to it.
|
|
* </p>
|
|
*
|
|
* @param reqInfo Missing extension information
|
|
* @param instInfo Older installed version information
|
|
*
|
|
* @return true if the installation is successful
|
|
*/
|
|
protected boolean installExtension(ExtensionInfo reqInfo,
|
|
ExtensionInfo instInfo)
|
|
throws ExtensionInstallationException
|
|
{
|
|
Vector<ExtensionInstallationProvider> currentProviders;
|
|
synchronized(providers) {
|
|
@SuppressWarnings("unchecked")
|
|
Vector<ExtensionInstallationProvider> tmp =
|
|
(Vector<ExtensionInstallationProvider>) providers.clone();
|
|
currentProviders = tmp;
|
|
}
|
|
for (Enumeration<ExtensionInstallationProvider> e = currentProviders.elements();
|
|
e.hasMoreElements();) {
|
|
ExtensionInstallationProvider eip = e.nextElement();
|
|
|
|
if (eip!=null) {
|
|
// delegate the installation to the provider
|
|
if (eip.installExtension(reqInfo, instInfo)) {
|
|
debug(reqInfo.name + " installation successful");
|
|
Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader)
|
|
Launcher.getLauncher().getClassLoader().getParent();
|
|
addNewExtensionsToClassLoader(cl);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
// We have tried all of our providers, noone could install this
|
|
// extension, we just return failure at this point
|
|
debug(reqInfo.name + " installation failed");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Checks if the extension, that is specified in the extension-list in
|
|
* the applet jar manifest, is already installed (i.e. exists in the
|
|
* extension directory).
|
|
* </p>
|
|
*
|
|
* @param extensionName extension name in the extension-list
|
|
*
|
|
* @return the extension if it exists in the extension directory
|
|
*/
|
|
private File checkExtensionExists(String extensionName) {
|
|
// Function added to fix bug 4504166
|
|
final String extName = extensionName;
|
|
final String[] fileExt = {".jar", ".zip"};
|
|
|
|
return AccessController.doPrivileged(
|
|
new PrivilegedAction<File>() {
|
|
public File run() {
|
|
try {
|
|
File fExtension;
|
|
File[] dirs = getExtDirs();
|
|
|
|
// Search the extension directories for the extension that is specified
|
|
// in the attribute extension-list in the applet jar manifest
|
|
for (int i=0;i<dirs.length;i++) {
|
|
for (int j=0;j<fileExt.length;j++) {
|
|
if (extName.toLowerCase().endsWith(fileExt[j])) {
|
|
fExtension = new File(dirs[i], extName);
|
|
} else {
|
|
fExtension = new File(dirs[i], extName+fileExt[j]);
|
|
}
|
|
debug("checkExtensionExists:fileName " + fExtension.getName());
|
|
if (fExtension.exists()) {
|
|
return fExtension;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
|
|
} catch(Exception e) {
|
|
debugException(e);
|
|
return null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* @return the java.ext.dirs property as a list of directory
|
|
* </p>
|
|
*/
|
|
private static File[] getExtDirs() {
|
|
String s = java.security.AccessController.doPrivileged(
|
|
new sun.security.action.GetPropertyAction("java.ext.dirs"));
|
|
|
|
File[] dirs;
|
|
if (s != null) {
|
|
StringTokenizer st =
|
|
new StringTokenizer(s, File.pathSeparator);
|
|
int count = st.countTokens();
|
|
debug("getExtDirs count " + count);
|
|
dirs = new File[count];
|
|
for (int i = 0; i < count; i++) {
|
|
dirs[i] = new File(st.nextToken());
|
|
debug("getExtDirs dirs["+i+"] "+ dirs[i]);
|
|
}
|
|
} else {
|
|
dirs = new File[0];
|
|
debug("getExtDirs dirs " + dirs);
|
|
}
|
|
debug("getExtDirs dirs.length " + dirs.length);
|
|
return dirs;
|
|
}
|
|
|
|
/*
|
|
* <p>
|
|
* Scan the directories and return all files installed in those
|
|
* </p>
|
|
* @param dirs list of directories to scan
|
|
*
|
|
* @return the list of files installed in all the directories
|
|
*/
|
|
private static File[] getExtFiles(File[] dirs) throws IOException {
|
|
Vector<File> urls = new Vector<File>();
|
|
for (int i = 0; i < dirs.length; i++) {
|
|
String[] files = dirs[i].list(new JarFilter());
|
|
if (files != null) {
|
|
debug("getExtFiles files.length " + files.length);
|
|
for (int j = 0; j < files.length; j++) {
|
|
File f = new File(dirs[i], files[j]);
|
|
urls.add(f);
|
|
debug("getExtFiles f["+j+"] "+ f);
|
|
}
|
|
}
|
|
}
|
|
File[] ua = new File[urls.size()];
|
|
urls.copyInto(ua);
|
|
debug("getExtFiles ua.length " + ua.length);
|
|
return ua;
|
|
}
|
|
|
|
/*
|
|
* <p>
|
|
* @return the list of installed extensions jar files
|
|
* </p>
|
|
*/
|
|
private File[] getInstalledExtensions() throws IOException {
|
|
return AccessController.doPrivileged(
|
|
new PrivilegedAction<File[]>() {
|
|
public File[] run() {
|
|
try {
|
|
return getExtFiles(getExtDirs());
|
|
} catch(IOException e) {
|
|
debug("Cannot get list of installed extensions");
|
|
debugException(e);
|
|
return new File[0];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
* <p>
|
|
* Add the newly installed jar file to the extension class loader.
|
|
* </p>
|
|
*
|
|
* @param cl the current installed extension class loader
|
|
*
|
|
* @return true if successful
|
|
*/
|
|
private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) {
|
|
try {
|
|
File[] installedExts = getInstalledExtensions();
|
|
for (int i=0;i<installedExts.length;i++) {
|
|
final File instFile = installedExts[i];
|
|
URL instURL = AccessController.doPrivileged(
|
|
new PrivilegedAction<URL>() {
|
|
public URL run() {
|
|
try {
|
|
return ParseUtil.fileToEncodedURL(instFile);
|
|
} catch (MalformedURLException e) {
|
|
debugException(e);
|
|
return null;
|
|
}
|
|
}
|
|
});
|
|
if (instURL != null) {
|
|
URL[] urls = cl.getURLs();
|
|
boolean found=false;
|
|
for (int j = 0; j<urls.length; j++) {
|
|
debug("URL["+j+"] is " + urls[j] + " looking for "+
|
|
instURL);
|
|
if (urls[j].toString().compareToIgnoreCase(
|
|
instURL.toString())==0) {
|
|
found=true;
|
|
debug("Found !");
|
|
}
|
|
}
|
|
if (!found) {
|
|
debug("Not Found ! adding to the classloader " +
|
|
instURL);
|
|
cl.addExtURL(instURL);
|
|
}
|
|
}
|
|
}
|
|
} catch (MalformedURLException e) {
|
|
e.printStackTrace();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
// let's continue with the next installed extension
|
|
}
|
|
return Boolean.TRUE;
|
|
}
|
|
|
|
// True to display all debug and trace messages
|
|
static final boolean DEBUG = false;
|
|
|
|
private static void debug(String s) {
|
|
if (DEBUG) {
|
|
System.err.println(s);
|
|
}
|
|
}
|
|
|
|
private void debugException(Throwable e) {
|
|
if (DEBUG) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
}
|