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 * <p> 49 * This class checks dependent extensions a particular jar file may have 50 * declared through its manifest attributes. 51 * </p> 52 * Jar file declared dependent extensions through the extension-list 53 * attribute. The extension-list contains a list of keys used to 54 * fetch the other attributes describing the required extension. 55 * If key is the extension key declared in the extension-list 56 * attribute, the following describing attribute can be found in 57 * the manifest : 58 * key-Extension-Name: (Specification package name) 59 * key-Specification-Version: (Specification-Version) 60 * key-Implementation-Version: (Implementation-Version) 61 * key-Implementation-Vendor-Id: (Imlementation-Vendor-Id) 62 * key-Implementation-Version: (Implementation version) 63 * key-Implementation-URL: (URL to download the requested extension) 64 * <p> 65 * This class also maintain versioning consistency of installed 66 * extensions dependencies declared in jar file manifest. 67 * </p> 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 * <p> 80 * Register an ExtensionInstallationProvider. The provider is responsible 81 * for handling the installation (upgrade) of any missing extensions. 82 * </p> 83 * @param eip ExtensionInstallationProvider implementation 84 */ 85 public synchronized static void addExtensionInstallationProvider 86 (ExtensionInstallationProvider eip) 87 { 88 if (providers == null) { 89 providers = new Vector<>(); 90 } 91 providers.add(eip); 92 } 93 94 /** 95 * <p> 96 * Unregister a previously installed installation provider 97 * </p> 98 */ 99 public synchronized static void removeExtensionInstallationProvider 100 (ExtensionInstallationProvider eip) 101 { 102 providers.remove(eip); 103 } 104 105 /** 106 * <p> 107 * Checks the dependencies of the jar file on installed extension. 108 * </p> 109 * @param jarFile containing the attriutes declaring the dependencies 110 */ 111 public static boolean checkExtensionsDependencies(JarFile jar) 112 { 113 if (providers == null) { 114 // no need to bother, nobody is registered to install missing 115 // extensions 116 return true; 117 } 118 119 try { 120 ExtensionDependency extDep = new ExtensionDependency(); 121 return extDep.checkExtensions(jar); 122 } catch (ExtensionInstallationException e) { 123 debug(e.getMessage()); 124 } 125 return false; 126 } 127 128 /* 129 * Check for all declared required extensions in the jar file 130 * manifest. 131 */ 132 protected boolean checkExtensions(JarFile jar) 133 throws ExtensionInstallationException 134 { 135 Manifest man; 136 try { 137 man = jar.getManifest(); 138 } catch (IOException e) { 139 return false; 140 } 141 142 if (man == null) { 143 // The applet does not define a manifest file, so 144 // we just assume all dependencies are satisfied. 145 return true; 146 } 147 148 boolean result = true; 149 Attributes attr = man.getMainAttributes(); 150 if (attr != null) { 151 // Let's get the list of declared dependencies 152 String value = attr.getValue(Name.EXTENSION_LIST); 153 if (value != null) { 154 StringTokenizer st = new StringTokenizer(value); 155 // Iterate over all declared dependencies 156 while (st.hasMoreTokens()) { 157 String extensionName = st.nextToken(); 158 debug("The file " + jar.getName() + 159 " appears to depend on " + extensionName); 160 // Sanity Check 161 String extName = extensionName + "-" + 162 Name.EXTENSION_NAME.toString(); 163 if (attr.getValue(extName) == null) { 164 debug("The jar file " + jar.getName() + 165 " appers to depend on " 166 + extensionName + " but does not define the " + 167 extName + " attribute in its manifest "); 168 169 } else { 170 if (!checkExtension(extensionName, attr)) { 171 debug("Failed installing " + extensionName); 172 result = false; 173 } 174 } 175 } 176 } else { 177 debug("No dependencies for " + jar.getName()); 178 } 179 } 180 return result; 181 } 182 183 184 /* 185 * <p> 186 * Check that a particular dependency on an extension is satisfied. 187 * </p> 188 * @param extensionName is the key used for the attributes in the manifest 189 * @param attr is the attributes of the manifest file 190 * 191 * @return true if the dependency is satisfied by the installed extensions 192 */ 193 protected synchronized boolean checkExtension(final String extensionName, 194 final Attributes attr) 195 throws ExtensionInstallationException 196 { 197 debug("Checking extension " + extensionName); 198 if (checkExtensionAgainstInstalled(extensionName, attr)) 199 return true; 200 201 debug("Extension not currently installed "); 202 ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr); 203 return installExtension(reqInfo, null); 204 } 205 206 /* 207 * <p> 208 * Check if a particular extension is part of the currently installed 209 * extensions. 210 * </p> 211 * @param extensionName is the key for the attributes in the manifest 212 * @param attr is the attributes of the manifest 213 * 214 * @return true if the requested extension is already installed 215 */ 216 boolean checkExtensionAgainstInstalled(String extensionName, 217 Attributes attr) 218 throws ExtensionInstallationException 219 { 220 File fExtension = checkExtensionExists(extensionName); 221 222 if (fExtension != null) { 223 // Extension already installed, just check against this one 224 try { 225 if (checkExtensionAgainst(extensionName, attr, fExtension)) 226 return true; 227 } catch (FileNotFoundException e) { 228 debugException(e); 229 } catch (IOException e) { 230 debugException(e); 231 } 232 return false; 233 234 } else { 235 // Not sure if extension is already installed, so check all the 236 // installed extension jar files to see if we get a match 237 238 File[] installedExts; 239 240 try { 241 // Get the list of installed extension jar files so we can 242 // compare the installed versus the requested extension 243 installedExts = getInstalledExtensions(); 244 } catch(IOException e) { 245 debugException(e); 246 return false; 247 } 248 249 for (int i=0;i<installedExts.length;i++) { 250 try { 251 if (checkExtensionAgainst(extensionName, attr, installedExts[i])) 252 return true; 253 } catch (FileNotFoundException e) { 254 debugException(e); 255 } catch (IOException e) { 256 debugException(e); 257 // let's continue with the next installed extension 258 } 259 } 260 } 261 return false; 262 } 263 264 /* 265 * <p> 266 * Check if the requested extension described by the attributes 267 * in the manifest under the key extensionName is compatible with 268 * the jar file. 269 * </p> 270 * 271 * @param extensionName key in the attribute list 272 * @param attr manifest file attributes 273 * @param file installed extension jar file to compare the requested 274 * extension against. 275 */ 276 protected boolean checkExtensionAgainst(String extensionName, 277 Attributes attr, 278 final File file) 279 throws IOException, 280 FileNotFoundException, 281 ExtensionInstallationException 282 { 283 284 debug("Checking extension " + extensionName + 285 " against " + file.getName()); 286 287 // Load the jar file ... 288 Manifest man; 289 try { 290 man = AccessController.doPrivileged( 291 new PrivilegedExceptionAction<Manifest>() { 292 public Manifest run() 293 throws IOException, FileNotFoundException { 294 if (!file.exists()) 295 throw new FileNotFoundException(file.getName()); 296 JarFile jarFile = new JarFile(file); 297 return jarFile.getManifest(); 298 } 299 }); 300 } catch(PrivilegedActionException e) { 301 if (e.getException() instanceof FileNotFoundException) 302 throw (FileNotFoundException) e.getException(); 303 throw (IOException) e.getException(); 304 } 305 306 // Construct the extension information object 307 ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr); 308 debug("Requested Extension : " + reqInfo); 309 310 int isCompatible = ExtensionInfo.INCOMPATIBLE; 311 ExtensionInfo instInfo = null; 312 313 if (man != null) { 314 Attributes instAttr = man.getMainAttributes(); 315 if (instAttr != null) { 316 instInfo = new ExtensionInfo(null, instAttr); 317 debug("Extension Installed " + instInfo); 318 isCompatible = instInfo.isCompatibleWith(reqInfo); 319 switch(isCompatible) { 320 case ExtensionInfo.COMPATIBLE: 321 debug("Extensions are compatible"); 322 return true; 323 324 case ExtensionInfo.INCOMPATIBLE: 325 debug("Extensions are incompatible"); 326 return false; 327 328 default: 329 // everything else 330 debug("Extensions require an upgrade or vendor switch"); 331 return installExtension(reqInfo, instInfo); 332 333 } 334 } 335 } 336 return false; 337 } 338 339 /* 340 * <p> 341 * An required extension is missing, if an ExtensionInstallationProvider is 342 * registered, delegate the installation of that particular extension to it. 343 * </p> 344 * 345 * @param reqInfo Missing extension information 346 * @param instInfo Older installed version information 347 * 348 * @return true if the installation is successful 349 */ 350 protected boolean installExtension(ExtensionInfo reqInfo, 351 ExtensionInfo instInfo) 352 throws ExtensionInstallationException 353 { 354 Vector<ExtensionInstallationProvider> currentProviders; 355 synchronized(providers) { 356 @SuppressWarnings("unchecked") 357 Vector<ExtensionInstallationProvider> tmp = 358 (Vector<ExtensionInstallationProvider>) providers.clone(); 359 currentProviders = tmp; 360 } 361 for (Enumeration<ExtensionInstallationProvider> e = currentProviders.elements(); 362 e.hasMoreElements();) { 363 ExtensionInstallationProvider eip = e.nextElement(); 364 365 if (eip!=null) { 366 // delegate the installation to the provider 367 if (eip.installExtension(reqInfo, instInfo)) { 368 debug(reqInfo.name + " installation successful"); 369 Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader) 370 Launcher.getLauncher().getClassLoader().getParent(); 371 addNewExtensionsToClassLoader(cl); 372 return true; 373 } 374 } 375 } 376 // We have tried all of our providers, noone could install this 377 // extension, we just return failure at this point 378 debug(reqInfo.name + " installation failed"); 379 return false; 380 } 381 382 /** 383 * <p> 384 * Checks if the extension, that is specified in the extension-list in 385 * the applet jar manifest, is already installed (i.e. exists in the 386 * extension directory). 387 * </p> 388 * 389 * @param extensionName extension name in the extension-list 390 * 391 * @return the extension if it exists in the extension directory 392 */ 393 private File checkExtensionExists(String extensionName) { 394 // Function added to fix bug 4504166 395 final String extName = extensionName; 396 final String[] fileExt = {".jar", ".zip"}; 397 398 return AccessController.doPrivileged( 399 new PrivilegedAction<File>() { 400 public File run() { 401 try { 402 File fExtension; 403 File[] dirs = getExtDirs(); 404 405 // Search the extension directories for the extension that is specified 406 // in the attribute extension-list in the applet jar manifest 407 for (int i=0;i<dirs.length;i++) { 408 for (int j=0;j<fileExt.length;j++) { 409 if (extName.toLowerCase().endsWith(fileExt[j])) { 410 fExtension = new File(dirs[i], extName); 411 } else { 412 fExtension = new File(dirs[i], extName+fileExt[j]); 413 } 414 debug("checkExtensionExists:fileName " + fExtension.getName()); 415 if (fExtension.exists()) { 416 return fExtension; 417 } 418 } 419 } 420 return null; 421 422 } catch(Exception e) { 423 debugException(e); 424 return null; 425 } 426 } 427 }); 428 } 429 430 /** 431 * <p> 432 * @return the java.ext.dirs property as a list of directory 433 * </p> 434 */ 435 private static File[] getExtDirs() { 436 String s = java.security.AccessController.doPrivileged( 437 new sun.security.action.GetPropertyAction("java.ext.dirs")); 438 439 File[] dirs; 440 if (s != null) { 441 StringTokenizer st = 442 new StringTokenizer(s, File.pathSeparator); 443 int count = st.countTokens(); 444 debug("getExtDirs count " + count); 445 dirs = new File[count]; 446 for (int i = 0; i < count; i++) { 447 dirs[i] = new File(st.nextToken()); 448 debug("getExtDirs dirs["+i+"] "+ dirs[i]); 449 } 450 } else { 451 dirs = new File[0]; 452 debug("getExtDirs dirs " + dirs); 453 } 454 debug("getExtDirs dirs.length " + dirs.length); 455 return dirs; 456 } 457 458 /* 459 * <p> 460 * Scan the directories and return all files installed in those 461 * </p> 462 * @param dirs list of directories to scan 463 * 464 * @return the list of files installed in all the directories 465 */ 466 private static File[] getExtFiles(File[] dirs) throws IOException { 467 Vector<File> urls = new Vector<File>(); 468 for (int i = 0; i < dirs.length; i++) { 469 String[] files = dirs[i].list(new JarFilter()); 470 if (files != null) { 471 debug("getExtFiles files.length " + files.length); 472 for (int j = 0; j < files.length; j++) { 473 File f = new File(dirs[i], files[j]); 474 urls.add(f); 475 debug("getExtFiles f["+j+"] "+ f); 476 } 477 } 478 } 479 File[] ua = new File[urls.size()]; 480 urls.copyInto(ua); 481 debug("getExtFiles ua.length " + ua.length); 482 return ua; 483 } 484 485 /* 486 * <p> 487 * @return the list of installed extensions jar files 488 * </p> 489 */ 490 private File[] getInstalledExtensions() throws IOException { 491 return AccessController.doPrivileged( 492 new PrivilegedAction<File[]>() { 493 public File[] run() { 494 try { 495 return getExtFiles(getExtDirs()); 496 } catch(IOException e) { 497 debug("Cannot get list of installed extensions"); 498 debugException(e); 499 return new File[0]; 500 } 501 } 502 }); 503 } 504 505 /* 506 * <p> 507 * Add the newly installed jar file to the extension class loader. 508 * </p> 509 * 510 * @param cl the current installed extension class loader 511 * 512 * @return true if successful 513 */ 514 private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) { 515 try { 516 File[] installedExts = getInstalledExtensions(); 517 for (int i=0;i<installedExts.length;i++) { 518 final File instFile = installedExts[i]; 519 URL instURL = AccessController.doPrivileged( 520 new PrivilegedAction<URL>() { 521 public URL run() { 522 try { 523 return ParseUtil.fileToEncodedURL(instFile); 524 } catch (MalformedURLException e) { 525 debugException(e); 526 return null; 527 } 528 } 529 }); 530 if (instURL != null) { 531 URL[] urls = cl.getURLs(); 532 boolean found=false; 533 for (int j = 0; j<urls.length; j++) { 534 debug("URL["+j+"] is " + urls[j] + " looking for "+ 535 instURL); 536 if (urls[j].toString().compareToIgnoreCase( 537 instURL.toString())==0) { 538 found=true; 539 debug("Found !"); 540 } 541 } 542 if (!found) { 543 debug("Not Found ! adding to the classloader " + 544 instURL); 545 cl.addExtURL(instURL); 546 } 547 } 548 } 549 } catch (MalformedURLException e) { 550 e.printStackTrace(); 551 } catch (IOException e) { 552 e.printStackTrace(); 553 // let's continue with the next installed extension 554 } 555 return Boolean.TRUE; 556 } 557 558 // True to display all debug and trace messages 559 static final boolean DEBUG = false; 560 561 private static void debug(String s) { 562 if (DEBUG) { 563 System.err.println(s); 564 } 565 } 566 567 private void debugException(Throwable e) { 568 if (DEBUG) { 569 e.printStackTrace(); 570 } 571 } 572 573 }