1 /* 2 * Copyright (c) 1998, 2011, 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); 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#appendToSystemClassPathSearch 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 File[] classPath = getClassPath(bootClassPath); 368 int len = classPath.length; 369 Set<File> seenDirs = new HashSet<File>(); 370 for (int i = 0; i < len; i++) { 371 File curEntry = classPath[i]; 372 // Negative test used to properly handle 373 // nonexistent jars on boot class path 374 if (!curEntry.isDirectory()) { 375 curEntry = curEntry.getParentFile(); 376 } 377 if (curEntry != null && seenDirs.add(curEntry)) { 378 MetaIndex.registerDirectory(curEntry); 379 } 380 } 381 return pathToURLs(classPath); 382 } 383 } 384 ); 385 } else { 386 urls = new URL[0]; 387 } 388 bcp = new URLClassPath(urls, factory); 389 } 390 } 391 392 public static URLClassPath getBootstrapClassPath() { 393 return BootClassPathHolder.bcp; 394 } 395 396 private static URL[] pathToURLs(File[] path) { 397 URL[] urls = new URL[path.length]; 398 for (int i = 0; i < path.length; i++) { 399 urls[i] = getFileURL(path[i]); 400 } 401 // DEBUG 402 //for (int i = 0; i < urls.length; i++) { 403 // System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"'); 404 //} 405 return urls; 406 } 407 408 private static File[] getClassPath(String cp) { 409 File[] path; 410 if (cp != null) { 411 int count = 0, maxCount = 1; 412 int pos = 0, lastPos = 0; 413 // Count the number of separators first 414 while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) { 415 maxCount++; 416 lastPos = pos + 1; 417 } 418 path = new File[maxCount]; 419 lastPos = pos = 0; 420 // Now scan for each path component 421 while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) { 422 if (pos - lastPos > 0) { 423 path[count++] = new File(cp.substring(lastPos, pos)); 424 } else { 425 // empty path component translates to "." 426 path[count++] = new File("."); 427 } 428 lastPos = pos + 1; 429 } 430 // Make sure we include the last path component 431 if (lastPos < cp.length()) { 432 path[count++] = new File(cp.substring(lastPos)); 433 } else { 434 path[count++] = new File("."); 435 } 436 // Trim array to correct size 437 if (count != maxCount) { 438 File[] tmp = new File[count]; 439 System.arraycopy(path, 0, tmp, 0, count); 440 path = tmp; 441 } 442 } else { 443 path = new File[0]; 444 } 445 // DEBUG 446 //for (int i = 0; i < path.length; i++) { 447 // System.out.println("path[" + i + "] = " + '"' + path[i] + '"'); 448 //} 449 return path; 450 } 451 452 private static URLStreamHandler fileHandler; 453 454 static URL getFileURL(File file) { 455 try { 456 file = file.getCanonicalFile(); 457 } catch (IOException e) {} 458 459 try { 460 return ParseUtil.fileToEncodedURL(file); 461 } catch (MalformedURLException e) { 462 // Should never happen since we specify the protocol... 463 throw new InternalError(e); 464 } 465 } 466 467 /* 468 * The stream handler factory for loading system protocol handlers. 469 */ 470 private static class Factory implements URLStreamHandlerFactory { 471 private static String PREFIX = "sun.net.www.protocol"; 472 473 public URLStreamHandler createURLStreamHandler(String protocol) { 474 String name = PREFIX + "." + protocol + ".Handler"; 475 try { 476 Class<?> c = Class.forName(name); 477 return (URLStreamHandler)c.newInstance(); 478 } catch (ReflectiveOperationException e) { 479 throw new InternalError("could not load " + protocol + 480 "system protocol handler", e); 481 } 482 } 483 } 484 } 485 486 class PathPermissions extends PermissionCollection { 487 // use serialVersionUID from JDK 1.2.2 for interoperability 488 private static final long serialVersionUID = 8133287259134945693L; 489 490 private File path[]; 491 private Permissions perms; 492 493 URL codeBase; 494 495 PathPermissions(File path[]) 496 { 497 this.path = path; 498 this.perms = null; 499 this.codeBase = null; 500 } 501 502 URL getCodeBase() 503 { 504 return codeBase; 505 } 506 507 public void add(java.security.Permission permission) { 508 throw new SecurityException("attempt to add a permission"); 509 } 510 511 private synchronized void init() 512 { 513 if (perms != null) 514 return; 515 516 perms = new Permissions(); 517 518 // this is needed to be able to create the classloader itself! 519 perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); 520 521 // add permission to read any "java.*" property 522 perms.add(new java.util.PropertyPermission("java.*", 523 SecurityConstants.PROPERTY_READ_ACTION)); 524 525 AccessController.doPrivileged(new PrivilegedAction<Void>() { 526 public Void run() { 527 for (int i=0; i < path.length; i++) { 528 File f = path[i]; 529 String path; 530 try { 531 path = f.getCanonicalPath(); 532 } catch (IOException ioe) { 533 path = f.getAbsolutePath(); 534 } 535 if (i == 0) { 536 codeBase = Launcher.getFileURL(new File(path)); 537 } 538 if (f.isDirectory()) { 539 if (path.endsWith(File.separator)) { 540 perms.add(new FilePermission(path+"-", 541 SecurityConstants.FILE_READ_ACTION)); 542 } else { 543 perms.add(new FilePermission( 544 path + File.separator+"-", 545 SecurityConstants.FILE_READ_ACTION)); 546 } 547 } else { 548 int endIndex = path.lastIndexOf(File.separatorChar); 549 if (endIndex != -1) { 550 path = path.substring(0, endIndex+1) + "-"; 551 perms.add(new FilePermission(path, 552 SecurityConstants.FILE_READ_ACTION)); 553 } else { 554 // XXX? 555 } 556 } 557 } 558 return null; 559 } 560 }); 561 } 562 563 public boolean implies(java.security.Permission permission) { 564 if (perms == null) 565 init(); 566 return perms.implies(permission); 567 } 568 569 public java.util.Enumeration<Permission> elements() { 570 if (perms == null) 571 init(); 572 synchronized (perms) { 573 return perms.elements(); 574 } 575 } 576 577 public String toString() { 578 if (perms == null) 579 init(); 580 return perms.toString(); 581 } 582 }