1 /* 2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.misc; 27 28 import java.io.File; 29 import java.io.FilenameFilter; 30 import java.io.IOException; 31 import java.io.FileNotFoundException; 32 import java.util.StringTokenizer; 33 import java.util.Vector; 34 import java.util.Enumeration; 35 import java.util.jar.JarFile; 36 import java.util.jar.Manifest; 37 import java.util.jar.Attributes; 38 import java.util.jar.Attributes.Name; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import java.security.PrivilegedExceptionAction; 42 import java.security.PrivilegedActionException; 43 import java.net.URL; 44 import java.net.MalformedURLException; 45 import sun.net.www.ParseUtil; 46 47 /** 48 * This class checks dependent extensions a particular jar file may have 49 * declared through its manifest attributes. 50 * <p> 51 * Jar file declared dependent extensions through the extension-list 52 * attribute. The extension-list contains a list of keys used to 53 * fetch the other attributes describing the required extension. 54 * If key is the extension key declared in the extension-list 55 * attribute, the following describing attribute can be found in 56 * the manifest: 57 * <ul> 58 * <li>key-Extension-Name: (Specification package name)</li> 59 * <li>key-Specification-Version: (Specification-Version)</li> 60 * <li>key-Implementation-Version: (Implementation-Version)</li> 61 * <li>key-Implementation-Vendor-Id: (Imlementation-Vendor-Id)</li> 62 * <li>key-Implementation-Version: (Implementation version)</li> 63 * <li>key-Implementation-URL: (URL to download the requested extension)</li> 64 * </ul> 65 * <p> 66 * This class also maintain versioning consistency of installed 67 * extensions dependencies declared in jar file manifest. 68 * 69 * @deprecated this class will be removed in a future release. 70 * @author Jerome Dochez 71 */ 72 @Deprecated 73 public class ExtensionDependency { 74 75 /* Callbak interfaces to delegate installation of missing extensions */ 76 private static Vector<ExtensionInstallationProvider> providers; 77 78 /** 79 * Register an ExtensionInstallationProvider. The provider is responsible 80 * for handling the installation (upgrade) of any missing extensions. 81 * 82 * @param eip ExtensionInstallationProvider implementation 83 */ 84 public synchronized static void addExtensionInstallationProvider 85 (ExtensionInstallationProvider eip) 86 { 87 if (providers == null) { 88 providers = new Vector<>(); 89 } 90 providers.add(eip); 91 } 92 93 /** 94 * Unregister a previously installed installation provider 95 */ 96 public synchronized static void removeExtensionInstallationProvider 97 (ExtensionInstallationProvider eip) 98 { 99 providers.remove(eip); 100 } 101 102 /** 103 * Checks the dependencies of the jar file on installed extension. 104 * 105 * @param jar containing the attributes declaring the dependencies 106 */ 107 public static boolean checkExtensionsDependencies(JarFile jar) 108 { 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 { 131 Manifest man; 132 try { 133 man = jar.getManifest(); 134 } catch (IOException e) { 135 return false; 136 } 137 138 if (man == null) { 139 // The applet does not define a manifest file, so 140 // we just assume all dependencies are satisfied. 141 return true; 142 } 143 144 boolean result = true; 145 Attributes attr = man.getMainAttributes(); 146 if (attr != null) { 147 // Let's get the list of declared dependencies 148 String value = attr.getValue(Name.EXTENSION_LIST); 149 if (value != null) { 150 StringTokenizer st = new StringTokenizer(value); 151 // Iterate over all declared dependencies 152 while (st.hasMoreTokens()) { 153 String extensionName = st.nextToken(); 154 debug("The file " + jar.getName() + 155 " appears to depend on " + extensionName); 156 // Sanity Check 157 String extName = extensionName + "-" + 158 Name.EXTENSION_NAME.toString(); 159 if (attr.getValue(extName) == null) { 160 debug("The jar file " + jar.getName() + 161 " appers to depend on " 162 + extensionName + " but does not define the " + 163 extName + " 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 /* 181 * Check that a particular dependency on an extension is satisfied. 182 * 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 synchronized boolean checkExtension(final String extensionName, 189 final Attributes attr) 190 throws ExtensionInstallationException 191 { 192 debug("Checking extension " + extensionName); 193 if (checkExtensionAgainstInstalled(extensionName, attr)) 194 return true; 195 196 debug("Extension not currently installed "); 197 ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr); 198 return installExtension(reqInfo, null); 199 } 200 201 /* 202 * Check if a particular extension is part of the currently installed 203 * extensions. 204 * 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) 212 throws ExtensionInstallationException 213 { 214 File fExtension = checkExtensionExists(extensionName); 215 216 if (fExtension != null) { 217 // Extension already installed, just check against this one 218 try { 219 if (checkExtensionAgainst(extensionName, attr, fExtension)) 220 return true; 221 } catch (FileNotFoundException e) { 222 debugException(e); 223 } catch (IOException e) { 224 debugException(e); 225 } 226 return false; 227 228 } else { 229 // Not sure if extension is already installed, so check all the 230 // installed extension jar files to see if we get a match 231 232 File[] installedExts; 233 234 try { 235 // Get the list of installed extension jar files so we can 236 // compare the installed versus the requested extension 237 installedExts = getInstalledExtensions(); 238 } catch(IOException e) { 239 debugException(e); 240 return false; 241 } 242 243 for (int i=0;i<installedExts.length;i++) { 244 try { 245 if (checkExtensionAgainst(extensionName, attr, installedExts[i])) 246 return true; 247 } catch (FileNotFoundException e) { 248 debugException(e); 249 } catch (IOException e) { 250 debugException(e); 251 // let's continue with the next installed extension 252 } 253 } 254 } 255 return false; 256 } 257 258 /* 259 * Check if the requested extension described by the attributes 260 * in the manifest under the key extensionName is compatible with 261 * the jar file. 262 * 263 * @param extensionName key in the attribute list 264 * @param attr manifest file attributes 265 * @param file installed extension jar file to compare the requested 266 * extension against. 267 */ 268 protected boolean checkExtensionAgainst(String extensionName, 269 Attributes attr, 270 final File file) 271 throws IOException, 272 FileNotFoundException, 273 ExtensionInstallationException 274 { 275 276 debug("Checking extension " + extensionName + 277 " against " + file.getName()); 278 279 // Load the jar file ... 280 Manifest man; 281 try { 282 man = AccessController.doPrivileged( 283 new PrivilegedExceptionAction<Manifest>() { 284 public Manifest run() 285 throws IOException, FileNotFoundException { 286 if (!file.exists()) 287 throw new FileNotFoundException(file.getName()); 288 JarFile jarFile = new JarFile(file); 289 return jarFile.getManifest(); 290 } 291 }); 292 } catch(PrivilegedActionException e) { 293 if (e.getException() instanceof FileNotFoundException) 294 throw (FileNotFoundException) e.getException(); 295 throw (IOException) e.getException(); 296 } 297 298 // Construct the extension information object 299 ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr); 300 debug("Requested Extension : " + reqInfo); 301 302 int isCompatible = ExtensionInfo.INCOMPATIBLE; 303 ExtensionInfo instInfo = null; 304 305 if (man != null) { 306 Attributes instAttr = man.getMainAttributes(); 307 if (instAttr != null) { 308 instInfo = new ExtensionInfo(null, instAttr); 309 debug("Extension Installed " + instInfo); 310 isCompatible = instInfo.isCompatibleWith(reqInfo); 311 switch(isCompatible) { 312 case ExtensionInfo.COMPATIBLE: 313 debug("Extensions are compatible"); 314 return true; 315 316 case ExtensionInfo.INCOMPATIBLE: 317 debug("Extensions are incompatible"); 318 return false; 319 320 default: 321 // everything else 322 debug("Extensions require an upgrade or vendor switch"); 323 return installExtension(reqInfo, instInfo); 324 325 } 326 } 327 } 328 return false; 329 } 330 331 /* 332 * An required extension is missing, if an ExtensionInstallationProvider is 333 * registered, delegate the installation of that particular extension to it. 334 * 335 * @param reqInfo Missing extension information 336 * @param instInfo Older installed version information 337 * 338 * @return true if the installation is successful 339 */ 340 protected boolean installExtension(ExtensionInfo reqInfo, 341 ExtensionInfo instInfo) 342 throws ExtensionInstallationException 343 { 344 Vector<ExtensionInstallationProvider> currentProviders; 345 synchronized(providers) { 346 @SuppressWarnings("unchecked") 347 Vector<ExtensionInstallationProvider> tmp = 348 (Vector<ExtensionInstallationProvider>) providers.clone(); 349 currentProviders = tmp; 350 } 351 for (Enumeration<ExtensionInstallationProvider> e = currentProviders.elements(); 352 e.hasMoreElements();) { 353 ExtensionInstallationProvider eip = e.nextElement(); 354 355 if (eip!=null) { 356 // delegate the installation to the provider 357 if (eip.installExtension(reqInfo, instInfo)) { 358 debug(reqInfo.name + " installation successful"); 359 Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader) 360 Launcher.getLauncher().getClassLoader().getParent(); 361 addNewExtensionsToClassLoader(cl); 362 return true; 363 } 364 } 365 } 366 // We have tried all of our providers, noone could install this 367 // extension, we just return failure at this point 368 debug(reqInfo.name + " installation failed"); 369 return false; 370 } 371 372 /** 373 * Checks if the extension, that is specified in the extension-list in 374 * the applet jar manifest, is already installed (i.e. exists in the 375 * extension directory). 376 * 377 * @param extensionName extension name in the extension-list 378 * 379 * @return the extension if it exists in the extension directory 380 */ 381 private File checkExtensionExists(String extensionName) { 382 // Function added to fix bug 4504166 383 final String extName = extensionName; 384 final String[] fileExt = {".jar", ".zip"}; 385 386 return AccessController.doPrivileged( 387 new PrivilegedAction<File>() { 388 public File run() { 389 try { 390 File fExtension; 391 File[] dirs = getExtDirs(); 392 393 // Search the extension directories for the extension that is specified 394 // in the attribute extension-list in the applet jar manifest 395 for (int i=0;i<dirs.length;i++) { 396 for (int j=0;j<fileExt.length;j++) { 397 if (extName.toLowerCase().endsWith(fileExt[j])) { 398 fExtension = new File(dirs[i], extName); 399 } else { 400 fExtension = new File(dirs[i], extName+fileExt[j]); 401 } 402 debug("checkExtensionExists:fileName " + fExtension.getName()); 403 if (fExtension.exists()) { 404 return fExtension; 405 } 406 } 407 } 408 return null; 409 410 } catch(Exception e) { 411 debugException(e); 412 return null; 413 } 414 } 415 }); 416 } 417 418 /** 419 * @return the java.ext.dirs property as a list of directory 420 */ 421 private static File[] getExtDirs() { 422 String s = java.security.AccessController.doPrivileged( 423 new sun.security.action.GetPropertyAction("java.ext.dirs")); 424 425 File[] dirs; 426 if (s != null) { 427 StringTokenizer st = 428 new StringTokenizer(s, File.pathSeparator); 429 int count = st.countTokens(); 430 debug("getExtDirs count " + count); 431 dirs = new File[count]; 432 for (int i = 0; i < count; i++) { 433 dirs[i] = new File(st.nextToken()); 434 debug("getExtDirs dirs["+i+"] "+ dirs[i]); 435 } 436 } else { 437 dirs = new File[0]; 438 debug("getExtDirs dirs " + dirs); 439 } 440 debug("getExtDirs dirs.length " + dirs.length); 441 return dirs; 442 } 443 444 /* 445 * Scan the directories and return all files installed in those 446 * 447 * @param dirs list of directories to scan 448 * 449 * @return the list of files installed in all the directories 450 */ 451 private static File[] getExtFiles(File[] dirs) throws IOException { 452 Vector<File> urls = new Vector<File>(); 453 for (int i = 0; i < dirs.length; i++) { 454 String[] files = dirs[i].list(new JarFilter()); 455 if (files != null) { 456 debug("getExtFiles files.length " + files.length); 457 for (int j = 0; j < files.length; j++) { 458 File f = new File(dirs[i], files[j]); 459 urls.add(f); 460 debug("getExtFiles f["+j+"] "+ f); 461 } 462 } 463 } 464 File[] ua = new File[urls.size()]; 465 urls.copyInto(ua); 466 debug("getExtFiles ua.length " + ua.length); 467 return ua; 468 } 469 470 /* 471 * @return the list of installed extensions jar files 472 */ 473 private File[] getInstalledExtensions() throws IOException { 474 return AccessController.doPrivileged( 475 new PrivilegedAction<File[]>() { 476 public File[] run() { 477 try { 478 return getExtFiles(getExtDirs()); 479 } catch(IOException e) { 480 debug("Cannot get list of installed extensions"); 481 debugException(e); 482 return new File[0]; 483 } 484 } 485 }); 486 } 487 488 /* 489 * Add the newly installed jar file to the extension class loader. 490 * 491 * @param cl the current installed extension class loader 492 * 493 * @return true if successful 494 */ 495 private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) { 496 try { 497 File[] installedExts = getInstalledExtensions(); 498 for (int i=0;i<installedExts.length;i++) { 499 final File instFile = installedExts[i]; 500 URL instURL = AccessController.doPrivileged( 501 new PrivilegedAction<URL>() { 502 public URL run() { 503 try { 504 return ParseUtil.fileToEncodedURL(instFile); 505 } catch (MalformedURLException e) { 506 debugException(e); 507 return null; 508 } 509 } 510 }); 511 if (instURL != null) { 512 URL[] urls = cl.getURLs(); 513 boolean found=false; 514 for (int j = 0; j<urls.length; j++) { 515 debug("URL["+j+"] is " + urls[j] + " looking for "+ 516 instURL); 517 if (urls[j].toString().compareToIgnoreCase( 518 instURL.toString())==0) { 519 found=true; 520 debug("Found !"); 521 } 522 } 523 if (!found) { 524 debug("Not Found ! adding to the classloader " + 525 instURL); 526 cl.addExtURL(instURL); 527 } 528 } 529 } 530 } catch (MalformedURLException e) { 531 e.printStackTrace(); 532 } catch (IOException e) { 533 e.printStackTrace(); 534 // let's continue with the next installed extension 535 } 536 return Boolean.TRUE; 537 } 538 539 // True to display all debug and trace messages 540 static final boolean DEBUG = false; 541 542 private static void debug(String s) { 543 if (DEBUG) { 544 System.err.println(s); 545 } 546 } 547 548 private void debugException(Throwable e) { 549 if (DEBUG) { 550 e.printStackTrace(); 551 } 552 } 553 554 }