1 /* 2 * Copyright (c) 1998, 2014, 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.IOException; 30 import java.io.FilePermission; 31 import java.net.URL; 32 import java.net.URLClassLoader; 33 import java.net.MalformedURLException; 34 import java.net.URLStreamHandler; 35 import java.net.URLStreamHandlerFactory; 36 import java.util.HashSet; 37 import java.util.StringTokenizer; 38 import java.util.Set; 39 import java.util.Vector; 40 import java.security.AccessController; 41 import java.security.PrivilegedAction; 42 import java.security.PrivilegedExceptionAction; 43 import java.security.AccessControlContext; 44 import java.security.PermissionCollection; 45 import java.security.Permissions; 46 import java.security.Permission; 47 import java.security.ProtectionDomain; 48 import java.security.CodeSource; 49 import sun.security.util.SecurityConstants; 50 import sun.net.www.ParseUtil; 51 52 /** 53 * This class is used by the system to launch the main application. 54 Launcher */ 55 public class Launcher { 56 private static URLStreamHandlerFactory factory = new Factory(); 57 private static Launcher launcher = new Launcher(); 58 private static String bootClassPath = 59 System.getProperty("sun.boot.class.path"); 60 61 public static Launcher getLauncher() { 62 return launcher; 63 } 64 65 private ClassLoader loader; 66 67 public Launcher() { 68 // Create the extension class loader 69 ClassLoader extcl; 70 try { 71 extcl = ExtClassLoader.getExtClassLoader(); 72 } catch (IOException e) { 73 throw new InternalError( 74 "Could not create extension class loader", e); 75 } 76 77 // Now create the class loader to use to launch the application 78 try { 79 loader = AppClassLoader.getAppClassLoader(extcl); 80 } catch (IOException e) { 81 throw new InternalError( 82 "Could not create application class loader", e); 83 } 84 85 // Also set the context class loader for the primordial thread. 86 Thread.currentThread().setContextClassLoader(loader); 87 88 // Finally, install a security manager if requested 89 String s = System.getProperty("java.security.manager"); 90 if (s != null) { 91 SecurityManager sm = null; 92 if ("".equals(s) || "default".equals(s)) { 93 sm = new java.lang.SecurityManager(); 94 } else { 95 try { 96 sm = (SecurityManager)loader.loadClass(s).newInstance(); 97 } catch (IllegalAccessException e) { 98 } catch (InstantiationException e) { 99 } catch (ClassNotFoundException e) { 100 } catch (ClassCastException e) { 101 } 102 } 103 if (sm != null) { 104 System.setSecurityManager(sm); 105 } else { 106 throw new InternalError( 107 "Could not create SecurityManager: " + s); 108 } 109 } 110 } 111 112 /* 113 * Returns the class loader used to launch the main application. 114 */ 115 public ClassLoader getClassLoader() { 116 return loader; 117 } 118 119 /* 120 * The class loader used for loading installed extensions. 121 */ 122 static class ExtClassLoader extends URLClassLoader { 123 124 static { 125 ClassLoader.registerAsParallelCapable(); 126 } 127 128 /** 129 * create an ExtClassLoader. The ExtClassLoader is created 130 * within a context that limits which files it can read 131 */ 132 public static ExtClassLoader getExtClassLoader() throws IOException 133 { 134 final File[] dirs = getExtDirs(); 135 136 try { 137 // Prior implementations of this doPrivileged() block supplied 138 // aa synthesized ACC via a call to the private method 139 // ExtClassLoader.getContext(). 140 141 return AccessController.doPrivileged( 142 new PrivilegedExceptionAction<ExtClassLoader>() { 143 public ExtClassLoader run() throws IOException { 144 int len = dirs.length; 145 for (int i = 0; i < len; i++) { 146 MetaIndex.registerDirectory(dirs[i]); 147 } 148 return new ExtClassLoader(dirs); 149 } 150 }); 151 } catch (java.security.PrivilegedActionException e) { 152 throw (IOException) e.getException(); 153 } 154 } 155 156 void addExtURL(URL url) { 157 super.addURL(url); 158 } 159 160 /* 161 * Creates a new ExtClassLoader for the specified directories. 162 */ 163 public ExtClassLoader(File[] dirs) throws IOException { 164 super(getExtURLs(dirs), null, factory); 165 } 166 167 private static File[] getExtDirs() { 168 String s = System.getProperty("java.ext.dirs"); 169 File[] dirs; 170 if (s != null) { 171 StringTokenizer st = 172 new StringTokenizer(s, File.pathSeparator); 173 int count = st.countTokens(); 174 dirs = new File[count]; 175 for (int i = 0; i < count; i++) { 176 dirs[i] = new File(st.nextToken()); 177 } 178 } else { 179 dirs = new File[0]; 180 } 181 return dirs; 182 } 183 184 private static URL[] getExtURLs(File[] dirs) throws IOException { 185 Vector<URL> urls = new Vector<URL>(); 186 for (int i = 0; i < dirs.length; i++) { 187 String[] files = dirs[i].list(); 188 if (files != null) { 189 for (int j = 0; j < files.length; j++) { 190 if (!files[j].equals("meta-index")) { 191 File f = new File(dirs[i], files[j]); 192 urls.add(getFileURL(f)); 193 } 194 } 195 } 196 } 197 URL[] ua = new URL[urls.size()]; 198 urls.copyInto(ua); 199 return ua; 200 } 201 202 /* 203 * Searches the installed extension directories for the specified 204 * library name. For each extension directory, we first look for 205 * the native library in the subdirectory whose name is the value 206 * of the system property <code>os.arch</code>. Failing that, we 207 * look in the extension directory itself. 208 */ 209 public String findLibrary(String name) { 210 name = System.mapLibraryName(name); 211 URL[] urls = super.getURLs(); 212 File prevDir = null; 213 for (int i = 0; i < urls.length; i++) { 214 // Get the ext directory from the URL 215 File dir = new File(urls[i].getPath()).getParentFile(); 216 if (dir != null && !dir.equals(prevDir)) { 217 // Look in architecture-specific subdirectory first 218 // Read from the saved system properties to avoid deadlock 219 String arch = VM.getSavedProperty("os.arch"); 220 if (arch != null) { 221 File file = new File(new File(dir, arch), name); 222 if (file.exists()) { 223 return file.getAbsolutePath(); 224 } 225 } 226 // Then check the extension directory 227 File file = new File(dir, name); 228 if (file.exists()) { 229 return file.getAbsolutePath(); 230 } 231 } 232 prevDir = dir; 233 } 234 return null; 235 } 236 237 private static AccessControlContext getContext(File[] dirs) 238 throws IOException 239 { 240 PathPermissions perms = 241 new PathPermissions(dirs); 242 243 ProtectionDomain domain = new ProtectionDomain( 244 new CodeSource(perms.getCodeBase(), 245 (java.security.cert.Certificate[]) null), 246 perms); 247 248 AccessControlContext acc = 249 new AccessControlContext(new ProtectionDomain[] { domain }); 250 251 return acc; 252 } 253 } 254 255 /** 256 * The class loader used for loading from java.class.path. 257 * runs in a restricted security context. 258 */ 259 static class AppClassLoader extends URLClassLoader { 260 261 static { 262 ClassLoader.registerAsParallelCapable(); 263 } 264 265 public static ClassLoader getAppClassLoader(final ClassLoader extcl) 266 throws IOException 267 { 268 final String s = System.getProperty("java.class.path"); 269 final File[] path = (s == null) ? new File[0] : getClassPath(s, true); 270 271 // Note: on bugid 4256530 272 // Prior implementations of this doPrivileged() block supplied 273 // a rather restrictive ACC via a call to the private method 274 // AppClassLoader.getContext(). This proved overly restrictive 275 // when loading classes. Specifically it prevent 276 // accessClassInPackage.sun.* grants from being honored. 277 // 278 return AccessController.doPrivileged( 279 new PrivilegedAction<AppClassLoader>() { 280 public AppClassLoader run() { 281 URL[] urls = 282 (s == null) ? new URL[0] : pathToURLs(path); 283 return new AppClassLoader(urls, extcl); 284 } 285 }); 286 } 287 288 /* 289 * Creates a new AppClassLoader 290 */ 291 AppClassLoader(URL[] urls, ClassLoader parent) { 292 super(urls, parent, factory); 293 } 294 295 /** 296 * Override loadClass so we can checkPackageAccess. 297 */ 298 public Class<?> loadClass(String name, boolean resolve) 299 throws ClassNotFoundException 300 { 301 int i = name.lastIndexOf('.'); 302 if (i != -1) { 303 SecurityManager sm = System.getSecurityManager(); 304 if (sm != null) { 305 sm.checkPackageAccess(name.substring(0, i)); 306 } 307 } 308 return (super.loadClass(name, resolve)); 309 } 310 311 /** 312 * allow any classes loaded from classpath to exit the VM. 313 */ 314 protected PermissionCollection getPermissions(CodeSource codesource) 315 { 316 PermissionCollection perms = super.getPermissions(codesource); 317 perms.add(new RuntimePermission("exitVM")); 318 return perms; 319 } 320 321 /** 322 * This class loader supports dynamic additions to the class path 323 * at runtime. 324 * 325 * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch 326 */ 327 private void appendToClassPathForInstrumentation(String path) { 328 assert(Thread.holdsLock(this)); 329 330 // addURL is a no-op if path already contains the URL 331 super.addURL( getFileURL(new File(path)) ); 332 } 333 334 /** 335 * create a context that can read any directories (recursively) 336 * mentioned in the class path. In the case of a jar, it has to 337 * be the directory containing the jar, not just the jar, as jar 338 * files might refer to other jar files. 339 */ 340 341 private static AccessControlContext getContext(File[] cp) 342 throws java.net.MalformedURLException 343 { 344 PathPermissions perms = 345 new PathPermissions(cp); 346 347 ProtectionDomain domain = 348 new ProtectionDomain(new CodeSource(perms.getCodeBase(), 349 (java.security.cert.Certificate[]) null), 350 perms); 351 352 AccessControlContext acc = 353 new AccessControlContext(new ProtectionDomain[] { domain }); 354 355 return acc; 356 } 357 } 358 359 private static class BootClassPathHolder { 360 static final URLClassPath bcp; 361 static { 362 URL[] urls; 363 if (bootClassPath != null) { 364 urls = AccessController.doPrivileged( 365 new PrivilegedAction<URL[]>() { 366 public URL[] run() { 367 // Skip empty path in boot class path i.e. not default to use CWD 368 File[] classPath = getClassPath(bootClassPath, false); 369 int len = classPath.length; 370 Set<File> seenDirs = new HashSet<File>(); 371 for (int i = 0; i < len; i++) { 372 File curEntry = classPath[i]; 373 // Negative test used to properly handle 374 // nonexistent jars on boot class path 375 if (!curEntry.isDirectory()) { 376 curEntry = curEntry.getParentFile(); 377 } 378 if (curEntry != null && seenDirs.add(curEntry)) { 379 MetaIndex.registerDirectory(curEntry); 380 } 381 } 382 return pathToURLs(classPath); 383 } 384 } 385 ); 386 } else { 387 urls = new URL[0]; 388 } 389 bcp = new URLClassPath(urls, factory); 390 } 391 } 392 393 public static URLClassPath getBootstrapClassPath() { 394 return BootClassPathHolder.bcp; 395 } 396 397 private static URL[] pathToURLs(File[] path) { 398 URL[] urls = new URL[path.length]; 399 for (int i = 0; i < path.length; i++) { 400 urls[i] = getFileURL(path[i]); 401 } 402 // DEBUG 403 //for (int i = 0; i < urls.length; i++) { 404 // System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"'); 405 //} 406 return urls; 407 } 408 409 private static File[] getClassPath(String cp, boolean defaultToCwd) { 410 File[] path; 411 if (cp != null) { 412 int count = 0, maxCount = 1; 413 int pos = 0, lastPos = 0; 414 // Count the number of separators first 415 while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) { 416 maxCount++; 417 lastPos = pos + 1; 418 } 419 path = new File[maxCount]; 420 lastPos = pos = 0; 421 // Now scan for each path component 422 while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) { 423 if (pos > lastPos) { 424 path[count++] = new File(cp.substring(lastPos, pos)); 425 } else if (defaultToCwd) { 426 // empty path component translates to "." 427 path[count++] = new File("."); 428 } 429 lastPos = pos + 1; 430 } 431 // Make sure we include the last path component 432 if (lastPos < cp.length()) { 433 path[count++] = new File(cp.substring(lastPos)); 434 } else if (defaultToCwd) { 435 path[count++] = new File("."); 436 } 437 // Trim array to correct size 438 if (count != maxCount) { 439 File[] tmp = new File[count]; 440 System.arraycopy(path, 0, tmp, 0, count); 441 path = tmp; 442 } 443 } else { 444 path = new File[0]; 445 } 446 // DEBUG 447 //for (int i = 0; i < path.length; i++) { 448 // System.out.println("path[" + i + "] = " + '"' + path[i] + '"'); 449 //} 450 return path; 451 } 452 453 private static URLStreamHandler fileHandler; 454 455 static URL getFileURL(File file) { 456 try { 457 file = file.getCanonicalFile(); 458 } catch (IOException e) {} 459 460 try { 461 return ParseUtil.fileToEncodedURL(file); 462 } catch (MalformedURLException e) { 463 // Should never happen since we specify the protocol... 464 throw new InternalError(e); 465 } 466 } 467 468 /* 469 * The stream handler factory for loading system protocol handlers. 470 */ 471 private static class Factory implements URLStreamHandlerFactory { 472 private static String PREFIX = "sun.net.www.protocol"; 473 474 public URLStreamHandler createURLStreamHandler(String protocol) { 475 String name = PREFIX + "." + protocol + ".Handler"; 476 try { 477 Class<?> c = Class.forName(name); 478 return (URLStreamHandler)c.newInstance(); 479 } catch (ReflectiveOperationException e) { 480 throw new InternalError("could not load " + protocol + 481 "system protocol handler", e); 482 } 483 } 484 } 485 } 486 487 class PathPermissions extends PermissionCollection { 488 // use serialVersionUID from JDK 1.2.2 for interoperability 489 private static final long serialVersionUID = 8133287259134945693L; 490 491 private File path[]; 492 private Permissions perms; 493 494 URL codeBase; 495 496 PathPermissions(File path[]) 497 { 498 this.path = path; 499 this.perms = null; 500 this.codeBase = null; 501 } 502 503 URL getCodeBase() 504 { 505 return codeBase; 506 } 507 508 public void add(java.security.Permission permission) { 509 throw new SecurityException("attempt to add a permission"); 510 } 511 512 private synchronized void init() 513 { 514 if (perms != null) 515 return; 516 517 perms = new Permissions(); 518 519 // this is needed to be able to create the classloader itself! 520 perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); 521 522 // add permission to read any "java.*" property 523 perms.add(new java.util.PropertyPermission("java.*", 524 SecurityConstants.PROPERTY_READ_ACTION)); 525 526 AccessController.doPrivileged(new PrivilegedAction<Void>() { 527 public Void run() { 528 for (int i=0; i < path.length; i++) { 529 File f = path[i]; 530 String path; 531 try { 532 path = f.getCanonicalPath(); 533 } catch (IOException ioe) { 534 path = f.getAbsolutePath(); 535 } 536 if (i == 0) { 537 codeBase = Launcher.getFileURL(new File(path)); 538 } 539 if (f.isDirectory()) { 540 if (path.endsWith(File.separator)) { 541 perms.add(new FilePermission(path+"-", 542 SecurityConstants.FILE_READ_ACTION)); 543 } else { 544 perms.add(new FilePermission( 545 path + File.separator+"-", 546 SecurityConstants.FILE_READ_ACTION)); 547 } 548 } else { 549 int endIndex = path.lastIndexOf(File.separatorChar); 550 if (endIndex != -1) { 551 path = path.substring(0, endIndex+1) + "-"; 552 perms.add(new FilePermission(path, 553 SecurityConstants.FILE_READ_ACTION)); 554 } else { 555 // XXX? 556 } 557 } 558 } 559 return null; 560 } 561 }); 562 } 563 564 public boolean implies(java.security.Permission permission) { 565 if (perms == null) 566 init(); 567 return perms.implies(permission); 568 } 569 570 public java.util.Enumeration<Permission> elements() { 571 if (perms == null) 572 init(); 573 synchronized (perms) { 574 return perms.elements(); 575 } 576 } 577 578 public String toString() { 579 if (perms == null) 580 init(); 581 return perms.toString(); 582 } 583 }