Logo Search packages:      
Sourcecode: cacao-oj6 version File versions

JNLPClassLoader.java

// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


package net.sourceforge.jnlp.runtime;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import java.security.*;
import java.lang.reflect.*;
import javax.jnlp.*;
import javax.swing.JOptionPane;


import java.security.cert.Certificate;

import net.sourceforge.jnlp.*;
import net.sourceforge.jnlp.cache.*;
import net.sourceforge.jnlp.security.*;
import net.sourceforge.jnlp.services.*;
import net.sourceforge.jnlp.tools.JarSigner;
import net.sourceforge.jnlp.tools.KeyTool;

/**
 * Classloader that takes it's resources from a JNLP file.  If the
 * JNLP file defines extensions, separate classloaders for these
 * will be created automatically.  Classes are loaded with the
 * security context when the classloader was created.
 *
 * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author
 * @version $Revision: 1.20 $ 
 */
00048 public class JNLPClassLoader extends URLClassLoader {

    // todo: initializePermissions should get the permissions from
    // extension classes too so that main file classes can load
    // resources in an extension.

    /** shortcut for resources */
00055     private static String R(String key) { return JNLPRuntime.getMessage(key); }

    /** map from JNLPFile url to shared classloader */
00058     private static Map urlToLoader = new HashMap(); // never garbage collected!

    /** number of times a classloader with native code is created */
00061     private static int nativeCounter = 0;

    /** the directory for native code */
00064     private File nativeDir = null; // if set, some native code exists

    /** security context */
00067     private AccessControlContext acc = AccessController.getContext();

    /** the permissions for the cached jar files */
00070     private List resourcePermissions;

    /** the app */
00073     private ApplicationInstance app = null; // here for faster lookup in security manager

    /** list of this, local and global loaders this loader uses */
00076     private JNLPClassLoader loaders[] = null; // ..[0]==this

    /** whether to strictly adhere to the spec or not */
00079     private boolean strict = true;

    /** loads the resources */
00082     private ResourceTracker tracker = new ResourceTracker(true); // prefetch

    /** the update policy for resources */
00085     private UpdatePolicy updatePolicy;

    /** the JNLP file */
00088     private JNLPFile file;

    /** the resources section */
00091     private ResourcesDesc resources;

    /** the security section */
00094     private SecurityDesc security;
    
    /** Permissions granted by the user during runtime. */
00097     private ArrayList<Permission> runtimePermissions = new ArrayList<Permission>();

    /** all jars not yet part of classloader or active */
00100     private List available = new ArrayList();

      /** all of the jar files that were verified */
00103       private ArrayList<String> verifiedJars = null;

      /** all of the jar files that were not verified */
00106       private ArrayList<String> unverifiedJars = null;

      /** the jarsigner tool to verify our jars */
00109       private JarSigner js = null;

      private boolean signing = false;

    /**
     * Create a new JNLPClassLoader from the specified file.
     *
     * @param file the JNLP file
     */
00118     protected JNLPClassLoader(JNLPFile file, UpdatePolicy policy) throws LaunchException {
        super(new URL[0], JNLPClassLoader.class.getClassLoader());

        if (JNLPRuntime.isDebug())
            System.out.println("New classloader: "+file.getFileLocation());

        this.file = file;
        this.updatePolicy = policy;
        this.resources = file.getResources();

        // initialize extensions
        initializeExtensions();

        // initialize permissions
        initializePermissions();

        initializeResources();

        setSecurity();

    }

00140     private void setSecurity() {
            /**
             * When we're trying to load an applet, file.getSecurity() will return
             * null since there is no jnlp file to specify permissions. We
             * determine security settings here, after trying to verify jars.
             */
            if (file instanceof PluginBridge) {
                  if (signing == true) {
                        this.security = new SecurityDesc(file, 
                              SecurityDesc.ALL_PERMISSIONS,
                              file.getCodeBase().getHost());
                  } else {
                        this.security = new SecurityDesc(file, 
                              SecurityDesc.SANDBOX_PERMISSIONS, 
                              file.getCodeBase().getHost());
                  }
            } else { //regular jnlp file
                  
                  /**
                   * If the application is signed, then we set the SecurityDesc to the
                   * <security> tag in the jnlp file. Note that if an application is
                   * signed, but there is no <security> tag in the jnlp file, the
                   * application will get sandbox permissions.
                   * If the application is unsigned, we ignore the <security> tag and 
                   * use a sandbox instead. 
                   */
                  if (signing == true) {
                        this.security = file.getSecurity();
                  } else {
                        this.security = new SecurityDesc(file, 
                                    SecurityDesc.SANDBOX_PERMISSIONS, 
                                    file.getCodeBase().getHost());
                  }
            }
    }
    
    /**
     * Returns a JNLP classloader for the specified JNLP file.
     *
     * @param file the file to load classes for
     * @param policy the update policy to use when downloading resources
     */
00182     public static JNLPClassLoader getInstance(JNLPFile file, UpdatePolicy policy) throws LaunchException {
        JNLPClassLoader loader = null;
        URL location = file.getFileLocation();

        if (location != null)
            loader = (JNLPClassLoader) urlToLoader.get(location);

            try {
            if (loader == null)
                  loader = new JNLPClassLoader(file, policy);
            } catch (LaunchException e) {
                  throw e;
            }

        if (file.getInformation().isSharingAllowed())
            urlToLoader.put(location, loader);

        return loader;
    }

    /**
     * Returns a JNLP classloader for the JNLP file at the specified
     * location. 
     *
     * @param location the file's location
     * @param policy the update policy to use when downloading resources
     */
00209     public static JNLPClassLoader getInstance(URL location, UpdatePolicy policy) throws IOException, ParseException, LaunchException {
        JNLPClassLoader loader = (JNLPClassLoader) urlToLoader.get(location);

        if (loader == null)
            loader = getInstance(new JNLPFile(location, false, policy), policy);

        return loader;
    }

    /**
     * Load the extensions specified in the JNLP file.
     */
00221     void initializeExtensions() {
        ExtensionDesc[] ext = resources.getExtensions();

        List loaderList = new ArrayList();

        loaderList.add(this);

            //if (ext != null) {
            for (int i=0; i < ext.length; i++) {
                  try {
                        JNLPClassLoader loader = getInstance(ext[i].getLocation(), updatePolicy);
                  loaderList.add(loader);
                  }
                  catch (Exception ex) {
                  ex.printStackTrace();
                  }
            }
            //}

        loaders = (JNLPClassLoader[]) loaderList.toArray(new JNLPClassLoader[ loaderList.size()]);
    }

    /**
     * Make permission objects for the classpath.
     */
00246     void initializePermissions() {
        resourcePermissions = new ArrayList();

        JARDesc jars[] = resources.getJARs();
        for (int i=0; i < jars.length; i++) {
            Permission p = CacheUtil.getReadPermission(jars[i].getLocation(),
                                                       jars[i].getVersion());

            if (JNLPRuntime.isDebug()) {
                  if (p == null)
                        System.out.println("Unable to add permission for " + jars[i].getLocation());
                  else
                        System.out.println("Permission added: " + p.toString());
            }
            if (p != null)
                resourcePermissions.add(p);
        }
    }

    /**
     * Load all of the JARs used in this JNLP file into the
     * ResourceTracker for downloading.
     */
00269     void initializeResources() throws LaunchException {
        JARDesc jars[] = resources.getJARs();
            if (jars == null || jars.length == 0)
                  return;
            /*
            if (jars == null || jars.length == 0) {
                  throw new LaunchException(null, null, R("LSFatal"),
                                      R("LCInit"), R("LFatalVerification"), "No jars!");
            }
            */
        List initialJars = new ArrayList();

        for (int i=0; i < jars.length; i++) {
            available.add(jars[i]);

            if (jars[i].isEager())
                initialJars.add(jars[i]); // regardless of part

            tracker.addResource(jars[i].getLocation(), 
                                jars[i].getVersion(), 
                                JNLPRuntime.getDefaultUpdatePolicy()
                               );
        }

        if (strict)
            fillInPartJars(initialJars); // add in each initial part's lazy jars

            if (JNLPRuntime.isVerifying()) {

                  JarSigner js;
                  waitForJars(initialJars); //download the jars first.

                  try {
                        js = verifyJars(initialJars);
                  } catch (Exception e) {
                        //we caught an Exception from the JarSigner class.
                        //Note: one of these exceptions could be from not being able
                        //to read the cacerts or trusted.certs files.
                        e.printStackTrace();
                        throw new LaunchException(null, null, R("LSFatal"),
                              R("LCInit"), R("LFatalVerification"), R("LFatalVerificationInfo"));
                  }

                  //Case when at least one jar has some signing
                  if (js.anyJarsSigned()){
                        signing = true;

                        //user does not trust this publisher
                        if (!js.getAlreadyTrustPublisher()) {
                              if (!js.getRootInCacerts()) { //root cert is not in cacerts
                                    boolean b = SecurityWarningDialog.showCertWarningDialog(
                                          SecurityWarningDialog.AccessType.UNVERIFIED, file, js);
                                    if (!b)
                                          throw new LaunchException(null, null, R("LSFatal"), 
                                                R("LCLaunching"), R("LNotVerified"), "");
                              } else if (js.getRootInCacerts()) { //root cert is in cacerts
                                    boolean b = false;
                                    if (js.noSigningIssues())
                                          b = SecurityWarningDialog.showCertWarningDialog(
                                                      SecurityWarningDialog.AccessType.VERIFIED, file, js);
                                    else if (!js.noSigningIssues())
                                          b = SecurityWarningDialog.showCertWarningDialog(
                                                      SecurityWarningDialog.AccessType.SIGNING_ERROR, file, js);
                                    if (!b)
                                          throw new LaunchException(null, null, R("LSFatal"),
                                                R("LCLaunching"), R("LCancelOnUserRequest"), "");
                              }
                        } else {
                              /**
                               * If the user trusts this publisher (i.e. the publisher's certificate
                               * is in the user's trusted.certs file), we do not show any dialogs.
                               */
                        }
                  } else {

                        signing = false;
                        //otherwise this jar is simply unsigned -- make sure to ask
                        //for permission on certain actions
                  }
            }

        activateJars(initialJars);
    }

    /**
     * Add applet's codebase URL.  This allows compatibility with
     * applets that load resources from their codebase instead of
     * through JARs, but can slow down resource loading.  Resources
     * loaded from the codebase are not cached.
     */
00359     public void enableCodeBase() {
        addURL( file.getCodeBase() ); // nothing happens if called more that once?
    }

    /**
     * Sets the JNLP app this group is for; can only be called once.
     */
00366     public void setApplication(ApplicationInstance app) {
        if (this.app != null) {
            if (JNLPRuntime.isDebug()) {
                Exception ex = new IllegalStateException("Application can only be set once");
                ex.printStackTrace();
            }
            return;
        }

        this.app = app;
    }

    /**
     * Returns the JNLP app for this classloader
     */
00381     public ApplicationInstance getApplication() {
        return app;
    }

    /**
     * Returns the JNLP file the classloader was created from.
     */
00388     public JNLPFile getJNLPFile() {
        return file;
    }

    /**
     * Returns the permissions for the CodeSource.
     */
00395     protected PermissionCollection getPermissions(CodeSource cs) {
        Permissions result = new Permissions();

        // should check for extensions or boot, automatically give all
        // access w/o security dialog once we actually check certificates.

        // copy security permissions from SecurityDesc element
            if (security != null) {
            Enumeration e = security.getPermissions().elements();
            while (e.hasMoreElements())
                  result.add((Permission) e.nextElement());
            }

        // add in permission to read the cached JAR files
        for (int i=0; i < resourcePermissions.size(); i++)
            result.add((Permission) resourcePermissions.get(i));

        // add in the permissions that the user granted.
        for (int i=0; i < runtimePermissions.size(); i++)
            result.add(runtimePermissions.get(i));
        
        return result;
    }

    protected void addPermission(Permission p) {
      runtimePermissions.add(p);
    }
    
    /**
     * Adds to the specified list of JARS any other JARs that need
     * to be loaded at the same time as the JARs specified (ie, are
     * in the same part).
     */
00428     protected void fillInPartJars(List jars) {
        for (int i=0; i < jars.size(); i++) {
            String part = ((JARDesc) jars.get(i)).getPart();

            for (int a=0; a < available.size(); a++) {
                JARDesc jar = (JARDesc) available.get(a);

                if (part != null && part.equals(jar.getPart()))
                    if (!jars.contains(jar))
                        jars.add(jar);
            }
        }
    }

    /**
     * Ensures that the list of jars have all been transferred, and
     * makes them available to the classloader.  If a jar contains
     * native code, the libraries will be extracted and placed in
     * the path.
     *
     * @param jars the list of jars to load
     */
00450     protected void activateJars(final List jars) {
        PrivilegedAction activate = new PrivilegedAction() {

            public Object run() {
                // transfer the Jars
                waitForJars(jars);


                for (int i=0; i < jars.size(); i++) {
                    JARDesc jar = (JARDesc) jars.get(i);

                    available.remove(jar);

                    // add jar
                    File localFile = tracker.getCacheFile(jar.getLocation());
                    try {
                        URL location = jar.getLocation(); // non-cacheable, use source location
                        if (localFile != null)
                            location = localFile.toURL(); // cached file

                        addURL(location);

                        if (JNLPRuntime.isDebug())
                            System.err.println("Activate jar: "+location);
                    }
                    catch (Exception ex) {
                        if (JNLPRuntime.isDebug())
                            ex.printStackTrace();
                    }

                    if (jar.isNative())
                        activateNative(jar);
                }

                return null;
            }
        };

        AccessController.doPrivileged(activate, acc);
    }

    /**
     * Enable the native code contained in a JAR by copying the
     * native files into the filesystem.  Called in the security
     * context of the classloader.
     */
00496     protected void activateNative(JARDesc jar) {
        if (JNLPRuntime.isDebug())
            System.out.println("Activate native: "+jar.getLocation());

        File localFile = tracker.getCacheFile(jar.getLocation());
        if (localFile == null)
            return;

        if (nativeDir == null)
            nativeDir = getNativeDir();

        try {
            JarFile jarFile = new JarFile(localFile, false);
            Enumeration entries = jarFile.entries();

            while (entries.hasMoreElements()) {
                JarEntry e = (JarEntry) entries.nextElement();

                if (e.isDirectory() || e.getName().indexOf('/') != -1)
                    continue;

                File outFile = new File(nativeDir, e.getName());

                CacheUtil.streamCopy(jarFile.getInputStream(e),
                                     new FileOutputStream(outFile));
            }
        }
        catch (IOException ex) {
            if (JNLPRuntime.isDebug())
                ex.printStackTrace();
        }
    }

    /**
     * Return the base directory to store native code files in.
     * This method does not need to return the same directory across
     * calls.
     */
00534     protected File getNativeDir() {
        nativeDir = new File(System.getProperty("java.io.tmpdir") 
                             + File.separator + "netx-native-" 
                             + (new Random().nextInt() & 0xFFFF));

        if (!nativeDir.mkdirs()) 
            return null;
        else
            return nativeDir;
    }

    /**
     * Return the absolute path to the native library.
     */
00548     protected String findLibrary(String lib) {
        if (nativeDir == null)
            return null;

        String syslib = System.mapLibraryName(lib);

        File target = new File(nativeDir, syslib);
        if (target.exists())
            return target.toString();
        else {
            String result = super.findLibrary(lib);
            if (result != null)
                return result;

            return findLibraryExt(lib);
        }
    }

    /**
     * Try to find the library path from another peer classloader.
     */
00569     protected String findLibraryExt(String lib) {
        for (int i=0; i < loaders.length; i++) {
            String result = null;

            if (loaders[i] != this)
                result = loaders[i].findLibrary(lib);

            if (result != null)
                return result;
        }

        return null;
    }

    /**
     * Wait for a group of JARs, and send download events if there
     * is a download listener or display a progress window otherwise.
     *
     * @param jars the jars
     */
00589     private void waitForJars(List jars) {
        URL urls[] = new URL[jars.size()];

        for (int i=0; i < jars.size(); i++) {
            JARDesc jar = (JARDesc) jars.get(i);

            urls[i] = jar.getLocation();
        }

        CacheUtil.waitForResources(app, tracker, urls, file.getTitle());
    }

    /**
       * Verifies code signing of jars to be used.
       *
       * @param jars the jars to be verified.
       */
00606       private JarSigner verifyJars(List<JARDesc> jars) throws Exception {
      
            js = new JarSigner();
            js.verifyJars(jars, tracker);
            return js;
      }

    /**
     * Find the loaded class in this loader or any of its extension loaders.
     */
00616     protected Class findLoadedClassAll(String name) {
        for (int i=0; i < loaders.length; i++) {
            Class result = null;

            if (loaders[i] == this)
                result = super.findLoadedClass(name);
            else
                result = loaders[i].findLoadedClassAll(name);

            if (result != null)
                return result;
        }

        return null;
    }

    /**
     * Find a JAR in the shared 'extension' classloaders, this
     * classloader, or one of the classloaders for the JNLP file's
     * extensions.
     */
00637     public Class loadClass(String name) throws ClassNotFoundException {
        Class result = findLoadedClassAll(name);

        // try parent classloader
        if (result == null) {
            try {
                ClassLoader parent = getParent();
                if (parent == null)
                    parent = ClassLoader.getSystemClassLoader();

                return parent.loadClass(name);
            }
            catch (ClassNotFoundException ex) { }
        }

        // filter out 'bad' package names like java, javax
        // validPackage(name);

        // search this and the extension loaders
        if (result == null)
            result = loadClassExt(name);

        return result;
    }

    /**
     * Find the class in this loader or any of its extension loaders.
     */
00665     protected Class findClass(String name) throws ClassNotFoundException {
        for (int i=0; i < loaders.length; i++) {
            try {
                if (loaders[i] == this)
                    return super.findClass(name);
                else
                    return loaders[i].findClass(name);
            }
            catch(ClassNotFoundException ex) { }
        }

        throw new ClassNotFoundException(name);
    }

    /**
     * Search for the class by incrementally adding resources to the
     * classloader and its extension classloaders until the resource
     * is found.
     */
00684     private Class loadClassExt(String name) throws ClassNotFoundException {
        // make recursive
        addAvailable();

        // find it
        try {
            return findClass(name);
        }
        catch(ClassNotFoundException ex) {
        }

        // add resources until found
        while (true) {
            JNLPClassLoader addedTo = addNextResource();

            if (addedTo == null)
                throw new ClassNotFoundException(name);

            try {
                return addedTo.findClass(name);
            }
            catch(ClassNotFoundException ex) {
            }
        }
    }

    /**
     * Finds the resource in this, the parent, or the extension
     * class loaders.
     */
00714     public URL getResource(String name) {
        URL result = super.getResource(name);

        for (int i=1; i < loaders.length; i++)
            if (result == null)
                result = loaders[i].getResource(name);

        return result;
    }

    /**
     * Finds the resource in this, the parent, or the extension
     * class loaders.
     */
00728     public Enumeration findResources(String name) throws IOException {
        Vector resources = new Vector();

        for (int i=0; i < loaders.length; i++) {
            Enumeration e;

            if (loaders[i] == this)
                e = super.findResources(name);
            else 
                e = loaders[i].findResources(name);

            while (e.hasMoreElements())
                resources.add(e.nextElement());
        }

        return resources.elements();
    }

    /**
     * Adds whatever resources have already been downloaded in the
     * background.
     */
00750     protected void addAvailable() {
        // go through available, check tracker for it and all of its
        // part brothers being available immediately, add them.

        for (int i=1; i < loaders.length; i++) {
            loaders[i].addAvailable();
        }
    }

    /**
     * Adds the next unused resource to the classloader.  That
     * resource and all those in the same part will be downloaded
     * and added to the classloader before returning.  If there are
     * no more resources to add, the method returns immediately.
     *
     * @return the classloader that resources were added to, or null
     */
00767     protected JNLPClassLoader addNextResource() {
        if (available.size() == 0) {
            for (int i=1; i < loaders.length; i++) {
                JNLPClassLoader result = loaders[i].addNextResource();

                if (result != null)
                    return result;
            }
            return null;
        }

        // add jar
        List jars = new ArrayList();
        jars.add(available.get(0));

        fillInPartJars(jars);

            
            activateJars(jars);

        return this;
    }

    // this part compatibility with previous classloader
    /**
     * @deprecated
     */
00794     public String getExtensionName() {
        String result = file.getInformation().getTitle();

        if (result == null)
            result = file.getInformation().getDescription();
        if (result == null && file.getFileLocation() != null)
            result = file.getFileLocation().toString();
        if (result == null && file.getCodeBase() != null)
            result = file.getCodeBase().toString();

        return result;
    }

    /**
     * @deprecated
     */
00810     public String getExtensionHREF() {
        return file.getFileLocation().toString();
    }

      public boolean getSigning() {
            return signing;
      }

      protected SecurityDesc getSecurity() {
            return security;
      }
}



Generated by  Doxygen 1.6.0   Back to index