Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/misc/ExtensionDependency.java
38829 views
/*1* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.misc;2627import java.io.File;28import java.io.FilenameFilter;29import java.io.IOException;30import java.io.FileNotFoundException;31import java.util.StringTokenizer;32import java.util.Vector;33import java.util.Enumeration;34import java.util.jar.JarFile;35import java.util.jar.Manifest;36import java.util.jar.Attributes;37import java.util.jar.Attributes.Name;38import java.security.AccessController;39import java.security.PrivilegedAction;40import java.security.PrivilegedExceptionAction;41import java.security.PrivilegedActionException;42import java.net.URL;43import java.net.MalformedURLException;44import sun.net.www.ParseUtil;4546/**47* <p>48* This class checks dependent extensions a particular jar file may have49* declared through its manifest attributes.50* </p>51* Jar file declared dependent extensions through the extension-list52* attribute. The extension-list contains a list of keys used to53* fetch the other attributes describing the required extension.54* If key is the extension key declared in the extension-list55* attribute, the following describing attribute can be found in56* the manifest :57* key-Extension-Name: (Specification package name)58* key-Specification-Version: (Specification-Version)59* key-Implementation-Version: (Implementation-Version)60* key-Implementation-Vendor-Id: (Imlementation-Vendor-Id)61* key-Implementation-Version: (Implementation version)62* key-Implementation-URL: (URL to download the requested extension)63* <p>64* This class also maintain versioning consistency of installed65* extensions dependencies declared in jar file manifest.66* </p>67* @author Jerome Dochez68*/69public class ExtensionDependency {7071/* Callbak interfaces to delegate installation of missing extensions */72private static Vector<ExtensionInstallationProvider> providers;7374/**75* <p>76* Register an ExtensionInstallationProvider. The provider is responsible77* for handling the installation (upgrade) of any missing extensions.78* </p>79* @param eip ExtensionInstallationProvider implementation80*/81public synchronized static void addExtensionInstallationProvider82(ExtensionInstallationProvider eip)83{84if (providers == null) {85providers = new Vector<>();86}87providers.add(eip);88}8990/**91* <p>92* Unregister a previously installed installation provider93* </p>94*/95public synchronized static void removeExtensionInstallationProvider96(ExtensionInstallationProvider eip)97{98providers.remove(eip);99}100101/**102* <p>103* Checks the dependencies of the jar file on installed extension.104* </p>105* @param jarFile containing the attriutes declaring the dependencies106*/107public static boolean checkExtensionsDependencies(JarFile jar)108{109if (providers == null) {110// no need to bother, nobody is registered to install missing111// extensions112return true;113}114115try {116ExtensionDependency extDep = new ExtensionDependency();117return extDep.checkExtensions(jar);118} catch (ExtensionInstallationException e) {119debug(e.getMessage());120}121return false;122}123124/*125* Check for all declared required extensions in the jar file126* manifest.127*/128protected boolean checkExtensions(JarFile jar)129throws ExtensionInstallationException130{131Manifest man;132try {133man = jar.getManifest();134} catch (IOException e) {135return false;136}137138if (man == null) {139// The applet does not define a manifest file, so140// we just assume all dependencies are satisfied.141return true;142}143144boolean result = true;145Attributes attr = man.getMainAttributes();146if (attr != null) {147// Let's get the list of declared dependencies148String value = attr.getValue(Name.EXTENSION_LIST);149if (value != null) {150StringTokenizer st = new StringTokenizer(value);151// Iterate over all declared dependencies152while (st.hasMoreTokens()) {153String extensionName = st.nextToken();154debug("The file " + jar.getName() +155" appears to depend on " + extensionName);156// Sanity Check157String extName = extensionName + "-" +158Name.EXTENSION_NAME.toString();159if (attr.getValue(extName) == null) {160debug("The jar file " + jar.getName() +161" appers to depend on "162+ extensionName + " but does not define the " +163extName + " attribute in its manifest ");164165} else {166if (!checkExtension(extensionName, attr)) {167debug("Failed installing " + extensionName);168result = false;169}170}171}172} else {173debug("No dependencies for " + jar.getName());174}175}176return result;177}178179180/*181* <p>182* Check that a particular dependency on an extension is satisfied.183* </p>184* @param extensionName is the key used for the attributes in the manifest185* @param attr is the attributes of the manifest file186*187* @return true if the dependency is satisfied by the installed extensions188*/189protected synchronized boolean checkExtension(final String extensionName,190final Attributes attr)191throws ExtensionInstallationException192{193debug("Checking extension " + extensionName);194if (checkExtensionAgainstInstalled(extensionName, attr))195return true;196197debug("Extension not currently installed ");198ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);199return installExtension(reqInfo, null);200}201202/*203* <p>204* Check if a particular extension is part of the currently installed205* extensions.206* </p>207* @param extensionName is the key for the attributes in the manifest208* @param attr is the attributes of the manifest209*210* @return true if the requested extension is already installed211*/212boolean checkExtensionAgainstInstalled(String extensionName,213Attributes attr)214throws ExtensionInstallationException215{216File fExtension = checkExtensionExists(extensionName);217218if (fExtension != null) {219// Extension already installed, just check against this one220try {221if (checkExtensionAgainst(extensionName, attr, fExtension))222return true;223} catch (FileNotFoundException e) {224debugException(e);225} catch (IOException e) {226debugException(e);227}228return false;229230} else {231// Not sure if extension is already installed, so check all the232// installed extension jar files to see if we get a match233234File[] installedExts;235236try {237// Get the list of installed extension jar files so we can238// compare the installed versus the requested extension239installedExts = getInstalledExtensions();240} catch(IOException e) {241debugException(e);242return false;243}244245for (int i=0;i<installedExts.length;i++) {246try {247if (checkExtensionAgainst(extensionName, attr, installedExts[i]))248return true;249} catch (FileNotFoundException e) {250debugException(e);251} catch (IOException e) {252debugException(e);253// let's continue with the next installed extension254}255}256}257return false;258}259260/*261* <p>262* Check if the requested extension described by the attributes263* in the manifest under the key extensionName is compatible with264* the jar file.265* </p>266*267* @param extensionName key in the attribute list268* @param attr manifest file attributes269* @param file installed extension jar file to compare the requested270* extension against.271*/272protected boolean checkExtensionAgainst(String extensionName,273Attributes attr,274final File file)275throws IOException,276FileNotFoundException,277ExtensionInstallationException278{279280debug("Checking extension " + extensionName +281" against " + file.getName());282283// Load the jar file ...284Manifest man;285try {286man = AccessController.doPrivileged(287new PrivilegedExceptionAction<Manifest>() {288public Manifest run()289throws IOException, FileNotFoundException {290if (!file.exists())291throw new FileNotFoundException(file.getName());292JarFile jarFile = new JarFile(file);293return jarFile.getManifest();294}295});296} catch(PrivilegedActionException e) {297if (e.getException() instanceof FileNotFoundException)298throw (FileNotFoundException) e.getException();299throw (IOException) e.getException();300}301302// Construct the extension information object303ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);304debug("Requested Extension : " + reqInfo);305306int isCompatible = ExtensionInfo.INCOMPATIBLE;307ExtensionInfo instInfo = null;308309if (man != null) {310Attributes instAttr = man.getMainAttributes();311if (instAttr != null) {312instInfo = new ExtensionInfo(null, instAttr);313debug("Extension Installed " + instInfo);314isCompatible = instInfo.isCompatibleWith(reqInfo);315switch(isCompatible) {316case ExtensionInfo.COMPATIBLE:317debug("Extensions are compatible");318return true;319320case ExtensionInfo.INCOMPATIBLE:321debug("Extensions are incompatible");322return false;323324default:325// everything else326debug("Extensions require an upgrade or vendor switch");327return installExtension(reqInfo, instInfo);328329}330}331}332return false;333}334335/*336* <p>337* An required extension is missing, if an ExtensionInstallationProvider is338* registered, delegate the installation of that particular extension to it.339* </p>340*341* @param reqInfo Missing extension information342* @param instInfo Older installed version information343*344* @return true if the installation is successful345*/346protected boolean installExtension(ExtensionInfo reqInfo,347ExtensionInfo instInfo)348throws ExtensionInstallationException349{350Vector<ExtensionInstallationProvider> currentProviders;351synchronized(providers) {352@SuppressWarnings("unchecked")353Vector<ExtensionInstallationProvider> tmp =354(Vector<ExtensionInstallationProvider>) providers.clone();355currentProviders = tmp;356}357for (Enumeration<ExtensionInstallationProvider> e = currentProviders.elements();358e.hasMoreElements();) {359ExtensionInstallationProvider eip = e.nextElement();360361if (eip!=null) {362// delegate the installation to the provider363if (eip.installExtension(reqInfo, instInfo)) {364debug(reqInfo.name + " installation successful");365Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader)366Launcher.getLauncher().getClassLoader().getParent();367addNewExtensionsToClassLoader(cl);368return true;369}370}371}372// We have tried all of our providers, noone could install this373// extension, we just return failure at this point374debug(reqInfo.name + " installation failed");375return false;376}377378/**379* <p>380* Checks if the extension, that is specified in the extension-list in381* the applet jar manifest, is already installed (i.e. exists in the382* extension directory).383* </p>384*385* @param extensionName extension name in the extension-list386*387* @return the extension if it exists in the extension directory388*/389private File checkExtensionExists(String extensionName) {390// Function added to fix bug 4504166391final String extName = extensionName;392final String[] fileExt = {".jar", ".zip"};393394return AccessController.doPrivileged(395new PrivilegedAction<File>() {396public File run() {397try {398File fExtension;399File[] dirs = getExtDirs();400401// Search the extension directories for the extension that is specified402// in the attribute extension-list in the applet jar manifest403for (int i=0;i<dirs.length;i++) {404for (int j=0;j<fileExt.length;j++) {405if (extName.toLowerCase().endsWith(fileExt[j])) {406fExtension = new File(dirs[i], extName);407} else {408fExtension = new File(dirs[i], extName+fileExt[j]);409}410debug("checkExtensionExists:fileName " + fExtension.getName());411if (fExtension.exists()) {412return fExtension;413}414}415}416return null;417418} catch(Exception e) {419debugException(e);420return null;421}422}423});424}425426/**427* <p>428* @return the java.ext.dirs property as a list of directory429* </p>430*/431private static File[] getExtDirs() {432String s = java.security.AccessController.doPrivileged(433new sun.security.action.GetPropertyAction("java.ext.dirs"));434435File[] dirs;436if (s != null) {437StringTokenizer st =438new StringTokenizer(s, File.pathSeparator);439int count = st.countTokens();440debug("getExtDirs count " + count);441dirs = new File[count];442for (int i = 0; i < count; i++) {443dirs[i] = new File(st.nextToken());444debug("getExtDirs dirs["+i+"] "+ dirs[i]);445}446} else {447dirs = new File[0];448debug("getExtDirs dirs " + dirs);449}450debug("getExtDirs dirs.length " + dirs.length);451return dirs;452}453454/*455* <p>456* Scan the directories and return all files installed in those457* </p>458* @param dirs list of directories to scan459*460* @return the list of files installed in all the directories461*/462private static File[] getExtFiles(File[] dirs) throws IOException {463Vector<File> urls = new Vector<File>();464for (int i = 0; i < dirs.length; i++) {465String[] files = dirs[i].list(new JarFilter());466if (files != null) {467debug("getExtFiles files.length " + files.length);468for (int j = 0; j < files.length; j++) {469File f = new File(dirs[i], files[j]);470urls.add(f);471debug("getExtFiles f["+j+"] "+ f);472}473}474}475File[] ua = new File[urls.size()];476urls.copyInto(ua);477debug("getExtFiles ua.length " + ua.length);478return ua;479}480481/*482* <p>483* @return the list of installed extensions jar files484* </p>485*/486private File[] getInstalledExtensions() throws IOException {487return AccessController.doPrivileged(488new PrivilegedAction<File[]>() {489public File[] run() {490try {491return getExtFiles(getExtDirs());492} catch(IOException e) {493debug("Cannot get list of installed extensions");494debugException(e);495return new File[0];496}497}498});499}500501/*502* <p>503* Add the newly installed jar file to the extension class loader.504* </p>505*506* @param cl the current installed extension class loader507*508* @return true if successful509*/510private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) {511try {512File[] installedExts = getInstalledExtensions();513for (int i=0;i<installedExts.length;i++) {514final File instFile = installedExts[i];515URL instURL = AccessController.doPrivileged(516new PrivilegedAction<URL>() {517public URL run() {518try {519return ParseUtil.fileToEncodedURL(instFile);520} catch (MalformedURLException e) {521debugException(e);522return null;523}524}525});526if (instURL != null) {527URL[] urls = cl.getURLs();528boolean found=false;529for (int j = 0; j<urls.length; j++) {530debug("URL["+j+"] is " + urls[j] + " looking for "+531instURL);532if (urls[j].toString().compareToIgnoreCase(533instURL.toString())==0) {534found=true;535debug("Found !");536}537}538if (!found) {539debug("Not Found ! adding to the classloader " +540instURL);541cl.addExtURL(instURL);542}543}544}545} catch (MalformedURLException e) {546e.printStackTrace();547} catch (IOException e) {548e.printStackTrace();549// let's continue with the next installed extension550}551return Boolean.TRUE;552}553554// True to display all debug and trace messages555static final boolean DEBUG = false;556557private static void debug(String s) {558if (DEBUG) {559System.err.println(s);560}561}562563private void debugException(Throwable e) {564if (DEBUG) {565e.printStackTrace();566}567}568569}570571572