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