001/*
002 * @(#)Launcher.java    1.42 05/11/17
003 *
004 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
005 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
006 */
007
008package sun.misc;
009
010import java.io.File;
011import java.io.IOException;
012import java.io.FilePermission;
013import java.net.URL;
014import java.net.URLClassLoader;
015import java.net.MalformedURLException;
016import java.net.URLStreamHandler;
017import java.net.URLStreamHandlerFactory;
018import java.util.HashSet;
019import java.util.StringTokenizer;
020import java.util.Set;
021import java.util.Vector;
022import java.security.AccessController;
023import java.security.AllPermission;
024import java.security.PrivilegedAction;
025import java.security.PrivilegedExceptionAction;
026import java.security.AccessControlContext;
027import java.security.PermissionCollection;
028import java.security.Permissions;
029import java.security.Permission;
030import java.security.ProtectionDomain;
031import java.security.CodeSource;
032import sun.security.action.GetPropertyAction;
033import sun.security.util.SecurityConstants;
034import sun.net.www.ParseUtil;
035import sun.jkernel.Bundle;
036import sun.jkernel.DownloadManager;
037
038/**
039 * This class is used by the system to launch the main application.
040Launcher */
041public class Launcher {
042    private static URLStreamHandlerFactory factory = new Factory();
043    private static Launcher launcher = new Launcher();
044
045    public static Launcher getLauncher() {
046        return launcher;
047    }
048
049    private ClassLoader loader;
050
051    public Launcher() {
052        // Create the extension class loader
053        ClassLoader extcl;
054        try {
055            extcl = ExtClassLoader.getExtClassLoader();
056        } catch (IOException e) {
057            throw new InternalError(
058                "Could not create extension class loader");
059        }
060
061        // Now create the class loader to use to launch the application
062        try {
063            loader = AppClassLoader.getAppClassLoader(extcl);
064        } catch (IOException e) {
065            throw new InternalError(
066                "Could not create application class loader");
067        }
068
069        // Also set the context class loader for the primordial thread.
070        Thread.currentThread().setContextClassLoader(loader);
071
072        // Finally, install a security manager if requested
073        String s = System.getProperty("java.security.manager");
074        if (s != null) {
075            SecurityManager sm = null;
076            if ("".equals(s) || "default".equals(s)) {
077                sm = new java.lang.SecurityManager();
078            } else {
079                try {
080                    sm = (SecurityManager)loader.loadClass(s).newInstance();
081                } catch (IllegalAccessException e) {
082                } catch (InstantiationException e) {
083                } catch (ClassNotFoundException e) {
084                } catch (ClassCastException e) {
085                }
086            }
087            if (sm != null) {
088                System.setSecurityManager(sm);
089            } else {
090                throw new InternalError(
091                    "Could not create SecurityManager: " + s);
092            }
093        }
094    }
095
096    /*
097     * Returns the class loader used to launch the main application.
098     */
099    public ClassLoader getClassLoader() {
100        return loader;
101    }
102
103    public static void addURLToAppClassLoader(URL u) {
104        AccessController.checkPermission(new AllPermission());
105        ClassLoader loader = Launcher.getLauncher().getClassLoader();
106        ((Launcher.AppClassLoader) loader).addAppURL(u);
107    }
108
109
110    public static void addURLToExtClassLoader(URL u) {
111        AccessController.checkPermission(new AllPermission());
112        ClassLoader loader = Launcher.getLauncher().getClassLoader();
113        ((Launcher.ExtClassLoader) loader.getParent()).addExtURL(u);
114    }
115
116    /*
117     * The class loader used for loading installed extensions.
118     */
119    static class ExtClassLoader extends URLClassLoader {
120        private File[] dirs;
121
122        /**
123         * create an ExtClassLoader. The ExtClassLoader is created
124         * within a context that limits which files it can read
125         */
126        public static ExtClassLoader getExtClassLoader() throws IOException
127        {
128            final File[] dirs = getExtDirs();
129
130            try {
131                // Prior implementations of this doPrivileged() block supplied 
132                // aa synthesized ACC via a call to the private method
133                // ExtClassLoader.getContext().
134
135                return (ExtClassLoader) AccessController.doPrivileged(
136                     new PrivilegedExceptionAction() {
137                        public Object run() throws IOException {
138                            int len = dirs.length;
139                            for (int i = 0; i < len; i++) {
140                                MetaIndex.registerDirectory(dirs[i]);
141                            }
142                            return new ExtClassLoader(dirs);
143                        }
144                    });
145            } catch (java.security.PrivilegedActionException e) {
146                    throw (IOException) e.getException();
147            }
148        }
149        
150        void addExtURL(URL url) {
151                super.addURL(url);
152        }
153
154        /*
155         * Creates a new ExtClassLoader for the specified directories.
156         */
157        public ExtClassLoader(File[] dirs) throws IOException {
158            super(getExtURLs(dirs), null, factory);
159            this.dirs = dirs;
160        }
161
162        private static File[] getExtDirs() {
163            String s = System.getProperty("java.ext.dirs");
164            File[] dirs;
165            if (s != null) {
166                StringTokenizer st = 
167                    new StringTokenizer(s, File.pathSeparator);
168                int count = st.countTokens();
169                dirs = new File[count];
170                for (int i = 0; i < count; i++) {
171                    dirs[i] = new File(st.nextToken());
172                }
173            } else {
174                dirs = new File[0];
175            }
176            return dirs;
177        }
178
179        private static URL[] getExtURLs(File[] dirs) throws IOException {
180            Vector urls = new Vector();
181            for (int i = 0; i < dirs.length; i++) {
182                String[] files = dirs[i].list();
183                if (files != null) {
184                    for (int j = 0; j < files.length; j++) {
185                        if (!files[j].equals("meta-index")) {
186                            File f = new File(dirs[i], files[j]);
187                            urls.add(getFileURL(f));
188                        }
189                    }
190                }
191            }
192            URL[] ua = new URL[urls.size()];
193            urls.copyInto(ua);
194            return ua;
195        }
196
197        /*
198         * Searches the installed extension directories for the specified
199         * library name. For each extension directory, we first look for
200         * the native library in the subdirectory whose name is the value
201         * of the system property <code>os.arch</code>. Failing that, we
202         * look in the extension directory itself.
203         */
204        public String findLibrary(String name) {
205            name = System.mapLibraryName(name);
206            for (int i = 0; i < dirs.length; i++) {
207                // Look in architecture-specific subdirectory first
208                String arch = System.getProperty("os.arch");
209                if (arch != null) {
210                    File file = new File(new File(dirs[i], arch), name);
211                    if (file.exists()) {
212                        return file.getAbsolutePath();
213                    }
214                }
215                // Then check the extension directory
216                File file = new File(dirs[i], name);
217                if (file.exists()) {
218                    return file.getAbsolutePath();
219                }
220            }
221            return null;
222        }
223        
224        protected Class findClass(String name) throws ClassNotFoundException {
225            // Check for download before we look for it.  If DownloadManager ends up 
226            // downloading it, it will add it to our search path before we proceed
227            // to the findClass().
228            DownloadManager.getBootClassPathEntryForClass(name);
229            return super.findClass(name);
230        }
231
232        private static AccessControlContext getContext(File[] dirs) 
233            throws IOException
234        {
235            PathPermissions perms =
236                new PathPermissions(dirs);
237
238            ProtectionDomain domain = new ProtectionDomain(
239                new CodeSource(perms.getCodeBase(),
240                    (java.security.cert.Certificate[]) null),
241                perms);
242
243            AccessControlContext acc = 
244                new AccessControlContext(new ProtectionDomain[] { domain });
245
246            return acc;
247        }
248    }
249
250    /**
251     * The class loader used for loading from java.class.path.
252     * runs in a restricted security context.
253     */
254    static class AppClassLoader extends URLClassLoader {
255
256        public static ClassLoader getAppClassLoader(final ClassLoader extcl)
257            throws IOException
258        {
259            final String s = System.getProperty("java.class.path");
260            final File[] path = (s == null) ? new File[0] : getClassPath(s);
261            
262            // Note: on bugid 4256530
263            // Prior implementations of this doPrivileged() block supplied 
264            // a rather restrictive ACC via a call to the private method
265            // AppClassLoader.getContext(). This proved overly restrictive
266            // when loading  classes. Specifically it prevent
267            // accessClassInPackage.sun.* grants from being honored.
268            //
269            return (AppClassLoader) 
270                AccessController.doPrivileged(new PrivilegedAction() {
271                public Object run() {
272                    URL[] urls =
273                        (s == null) ? new URL[0] : pathToURLs(path);
274                    return new AppClassLoader(urls, extcl);
275                }
276            });
277        }
278
279        /*
280         * Creates a new AppClassLoader
281         */
282        AppClassLoader(URL[] urls, ClassLoader parent) {
283            super(urls, parent, factory);
284        }
285
286
287        /**
288         * Override loadClass so we can checkPackageAccess.
289         */
290        public synchronized Class loadClass(String name, boolean resolve)
291            throws ClassNotFoundException
292        {
293            DownloadManager.getBootClassPathEntryForClass(name);
294            int i = name.lastIndexOf('.');
295            if (i != -1) {
296                SecurityManager sm = System.getSecurityManager();
297                if (sm != null) {
298                    sm.checkPackageAccess(name.substring(0, i));
299                }
300            }
301            return (super.loadClass(name, resolve));
302        }
303
304        /**
305         * allow any classes loaded from classpath to exit the VM.
306         */
307        protected PermissionCollection getPermissions(CodeSource codesource)
308        {
309            PermissionCollection perms = super.getPermissions(codesource);
310            perms.add(new RuntimePermission("exitVM"));
311            return perms;
312        }
313
314        /**
315         * This class loader supports dynamic additions to the class path
316         * at runtime.
317         *
318         * @see java.lang.instrument.Instrumentation#appendToSystemClassPathSearch
319         */
320        private void appendToClassPathForInstrumentation(String path) {
321            assert(Thread.holdsLock(this));
322
323            // addURL is a no-op if path already contains the URL
324            super.addURL( getFileURL(new File(path)) );
325        }
326
327        /**
328         * create a context that can read any directories (recursively)
329         * mentioned in the class path. In the case of a jar, it has to 
330         * be the directory containing the jar, not just the jar, as jar
331         * files might refer to other jar files.
332         */
333
334        private static AccessControlContext getContext(File[] cp)
335            throws java.net.MalformedURLException 
336        {
337            PathPermissions perms =
338                new PathPermissions(cp);
339
340            ProtectionDomain domain =
341                new ProtectionDomain(new CodeSource(perms.getCodeBase(), 
342                    (java.security.cert.Certificate[]) null),
343                perms);
344
345            AccessControlContext acc = 
346                new AccessControlContext(new ProtectionDomain[] { domain });
347            
348            return acc;
349        }
350
351
352        void addAppURL(URL url) {
353                super.addURL(url);
354        }
355
356    }
357
358    private static URLClassPath bootstrapClassPath;
359
360    public static synchronized URLClassPath getBootstrapClassPath() {
361        if (bootstrapClassPath == null) {
362            String prop = (String)AccessController.doPrivileged(new GetPropertyAction("sun.boot.class.path"));
363            URL[] urls;
364            if (prop != null) {
365                final String path = prop;
366                urls = (URL[])AccessController.doPrivileged(
367                    new PrivilegedAction() {
368                        public Object run() {
369                            File[] classPath = getClassPath(path);
370                            int len = classPath.length;
371                            Set seenDirs = new HashSet();
372                            for (int i = 0; i < len; i++) {
373                                File curEntry = classPath[i];
374                                // Negative test used to properly handle
375                                // nonexistent jars on boot class path
376                                if (!curEntry.isDirectory()) {
377                                    curEntry = curEntry.getParentFile();
378                                }
379                                if (curEntry != null && seenDirs.add(curEntry)) {
380                                    MetaIndex.registerDirectory(curEntry);
381                                }
382                            }
383                            return pathToURLs(classPath);
384                        }
385                    }
386                );
387            } else {
388                urls = new URL[0];
389            }
390            
391            bootstrapClassPath = new URLClassPath(urls, factory);
392            final File[] additionalBootStrapPaths =
393                    DownloadManager.getAdditionalBootStrapPaths();
394            AccessController.doPrivileged(
395                    new PrivilegedAction() {
396                public Object run() {
397                    for (int i = 0; i < additionalBootStrapPaths.length; i++)
398                        bootstrapClassPath.addURL(getFileURL(additionalBootStrapPaths[i]));
399                    return null;
400                }
401            });
402        }
403        return bootstrapClassPath;
404    }
405    
406    
407    public static synchronized void flushBootstrapClassPath() {
408        bootstrapClassPath = null;
409    }
410    
411
412    private static URL[] pathToURLs(File[] path) {
413        URL[] urls = new URL[path.length];
414        for (int i = 0; i < path.length; i++) {
415            urls[i] = getFileURL(path[i]);
416        }
417        // DEBUG
418        //for (int i = 0; i < urls.length; i++) {
419        //  System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"');
420        //}
421        return urls;
422    }
423
424    private static File[] getClassPath(String cp) {
425        File[] path;
426        if (cp != null) {
427            int count = 0, maxCount = 1;
428            int pos = 0, lastPos = 0;
429            // Count the number of separators first
430            while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
431                maxCount++;
432                lastPos = pos + 1;
433            }
434            path = new File[maxCount];
435            lastPos = pos = 0;
436            // Now scan for each path component
437            while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
438                if (pos - lastPos > 0) {
439                    path[count++] = new File(cp.substring(lastPos, pos));
440                } else {
441                    // empty path component translates to "."
442                    path[count++] = new File(".");
443                }
444                lastPos = pos + 1;
445            }
446            // Make sure we include the last path component
447            if (lastPos < cp.length()) {
448                path[count++] = new File(cp.substring(lastPos));
449            } else {
450                path[count++] = new File(".");
451            }
452            // Trim array to correct size
453            if (count != maxCount) {
454                File[] tmp = new File[count];
455                System.arraycopy(path, 0, tmp, 0, count);
456                path = tmp;
457            }
458        } else {
459            path = new File[0];
460        }
461        // DEBUG
462        //for (int i = 0; i < path.length; i++) {
463        //  System.out.println("path[" + i + "] = " + '"' + path[i] + '"');
464        //}
465        return path;
466    }
467
468    private static URLStreamHandler fileHandler;
469
470    static URL getFileURL(File file) {
471        try {
472            file = file.getCanonicalFile();
473        } catch (IOException e) {} 
474
475        try {
476            return ParseUtil.fileToEncodedURL(file);
477        } catch (MalformedURLException e) {
478            // Should never happen since we specify the protocol...
479            throw new InternalError();
480        }
481    }
482
483    /*
484     * The stream handler factory for loading system protocol handlers.
485     */
486    private static class Factory implements URLStreamHandlerFactory {
487        private static String PREFIX = "sun.net.www.protocol";
488
489        public URLStreamHandler createURLStreamHandler(String protocol) {
490            String name = PREFIX + "." + protocol + ".Handler";
491            try {
492                Class c = Class.forName(name);
493                return (URLStreamHandler)c.newInstance();
494            } catch (ClassNotFoundException e) {
495                e.printStackTrace();
496            } catch (InstantiationException e) {
497                e.printStackTrace();
498            } catch (IllegalAccessException e) {
499                e.printStackTrace();
500            }
501            throw new InternalError("could not load " + protocol +
502                                    "system protocol handler");
503        }
504    }
505}
506
507class PathPermissions extends PermissionCollection {
508    // use serialVersionUID from JDK 1.2.2 for interoperability
509    private static final long serialVersionUID = 8133287259134945693L;
510
511    private File path[];
512    private Permissions perms;
513
514    URL codeBase;
515
516    PathPermissions(File path[])
517    {
518        this.path = path;
519        this.perms = null;
520        this.codeBase = null;
521    }
522
523    URL getCodeBase()
524    {
525        return codeBase;
526    }
527
528    public void add(java.security.Permission permission) {
529        throw new SecurityException("attempt to add a permission");
530    }
531
532    private synchronized void init()
533    {
534        if (perms != null)
535            return;
536
537        perms = new Permissions();
538
539        // this is needed to be able to create the classloader itself!
540        perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
541
542        // add permission to read any "java.*" property
543        perms.add(new java.util.PropertyPermission("java.*",
544            SecurityConstants.PROPERTY_READ_ACTION));
545
546        AccessController.doPrivileged(new PrivilegedAction() {
547            public Object run() {
548                for (int i=0; i < path.length; i++) {
549                    File f = path[i];
550                    String path;
551                    try {
552                        path = f.getCanonicalPath();
553                    } catch (IOException ioe) {
554                        path = f.getAbsolutePath();
555                    }
556                    if (i == 0) {
557                        codeBase = Launcher.getFileURL(new File(path));
558                    }
559                    if (f.isDirectory()) {
560                        if (path.endsWith(File.separator)) {
561                            perms.add(new FilePermission(path+"-",
562                                SecurityConstants.FILE_READ_ACTION));
563                        } else {
564                            perms.add(new FilePermission(
565                                path + File.separator+"-",
566                                SecurityConstants.FILE_READ_ACTION));
567                        }
568                    } else {
569                        int endIndex = path.lastIndexOf(File.separatorChar);
570                        if (endIndex != -1) {
571                            path = path.substring(0, endIndex+1) + "-";
572                            perms.add(new FilePermission(path,
573                                SecurityConstants.FILE_READ_ACTION));
574                        } else {
575                            // XXX?
576                        }
577                    }
578                }
579                return null;
580            }
581        });
582    }
583
584    public boolean implies(java.security.Permission permission) {
585        if (perms == null)
586            init();
587        return perms.implies(permission);
588    }
589
590    public java.util.Enumeration elements() {
591        if (perms == null)
592            init();
593        synchronized (perms) {
594            return perms.elements();
595        }
596    }
597
598    public String toString() {
599        if (perms == null)
600            init();
601        return perms.toString();
602    }
603}