home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 July / Chip_1998-07_cd.bin / zkuste / JBuilder / BDK / Win / bdk_sep97.exe / _SETUP.1 / SimpleClassLoader.java < prev    next >
Encoding:
Java Source  |  1997-09-10  |  13.8 KB  |  493 lines

  1. package sun.beanbox;
  2.  
  3. /**
  4.  * A class loader that is used byt the JarLoader to
  5.  * load the classes and resources.
  6.  *
  7.  * Class instances are given an cookie string to identify their
  8.  * resources but our client code creates a single instance only.  This
  9.  * is because it is impossible in general to pass values across from
  10.  * classes in one classloader to another.
  11.  *
  12.  * Some of the code used here probably should be part of JDK1.(2, I guess);
  13.  * much of it is in the sun.* classes in JDK1.1 but that is not good enough
  14.  * since our code only depends on jdk1.1.
  15.  *
  16.  * This class loader can load from:
  17.  *   - the system classes/resources (CLASSPATH)
  18.  *   - JAR files
  19.  *   - specific files
  20.  *   - specific overrides for resources
  21.  *
  22.  *  This code still needs a cleanup pass...
  23.  */
  24.  
  25. import java.io.*;
  26. import java.util.*;
  27. import java.util.zip.*;
  28. import java.net.*;
  29. import java.awt.*;
  30.  
  31. // Dependencies on sun.* classes;  the fewer the better
  32. import sun.awt.image.*;
  33.  
  34.  
  35. public class SimpleClassLoader extends ClassLoader {
  36.     public final static String urlPrefix = "SIMPLE";
  37.     private static final String protocolPathProp = "java.protocol.handler.pkgs";
  38.     private static boolean debug = false; // debugging
  39.     private static boolean keepLoading = true; // aggresively load classes
  40.     private String cookie;    // name of the jar file
  41.  
  42.     /*
  43.      * all instances, by cookie.  We really only instantiate one, see next, but
  44.      * we are leaving the machinery around in case we need it again later
  45.      */
  46.     private static Hashtable loaders = new Hashtable();
  47.  
  48.     /*
  49.      * The only SimpleClassLoader we actually instantiate
  50.      */
  51.     public static SimpleClassLoader ourLoader;
  52.  
  53.     /* 
  54.      * Additional directory from where to look for resources...
  55.      * (after CLASSPATH and loaded JARs).
  56.      * -- currently unused --
  57.      */
  58.     private String localResourceDirectory;
  59.  
  60.     /*
  61.      * Overrides for local resources
  62.      */
  63.     private Hashtable localOverrides = new Hashtable();
  64.  
  65.     /**
  66.      * Create a SipleClassLoader.  It is identified by a cookie string
  67.      */
  68.     private SimpleClassLoader(String cookie, String dir) {
  69.     this.cookie = cookie;
  70.     this.localResourceDirectory = dir;
  71.     loaders.put(cookie, this);
  72.     }
  73.  
  74.     /**
  75.      * Resource and Mime Type HashTables
  76.      */
  77.     private Hashtable resourceHash = new Hashtable();
  78.     private Hashtable mimeHash = new Hashtable();
  79.  
  80.     /** A hash table of AppletClassEntry's that define .class files in their
  81.      *  raw form.  We <B>do not</B> define these classes immediately
  82.      *  in defineClassFromBytes(), instead the byte arrays are kept
  83.      *  around and classes are defined lazily on demand, and the byte
  84.      *  array is removed from the Hashtable at that time.
  85.      *  <P>
  86.      *  By defining classes lazily, we prevent the case where a
  87.      *  derived class was found in a jar before its super class also
  88.      *  in the jar.  The derived class cannot be defined before it's
  89.      *  super class has even been loaded from the archive.
  90.      *
  91.      *  classes are hashed by fully qualified name - "my.package.MyApplet"
  92.      */
  93.  
  94.     private Hashtable rawClasses = new Hashtable();
  95.  
  96.  
  97.     /**
  98.      * Set some bytecodes as a definition for a class.
  99.      * Do not actually define the class until later
  100.      */
  101.  
  102.     public void defineClassFromBytes(String name, byte[] buf) {
  103.     rawClasses.put(name, buf);
  104.     }
  105.  
  106.     /**
  107.      * Define (& link) a class based on what was acquired previously in the JAR file
  108.      * Should catch all exceptions and return non-null iff definition is valid
  109.      */
  110.  
  111.     private Class applyDefinition(String name, boolean resolve) {
  112.     byte buf[] = (byte []) rawClasses.get(name);
  113.     rawClasses.remove(name); // release the bytecodes...
  114.     if (buf == null) {
  115.         return null;
  116.     } else {
  117.         Class c = null;
  118.         try {
  119.         c = super.defineClass(null, buf, 0, buf.length);
  120.         if (c != null && resolve) {
  121.             resolveClass(c);
  122.         }
  123.         } catch (ClassFormatError e) {
  124.         System.err.println("The definition for "+name+" in the JAR file");
  125.         System.err.println("has a format error.");
  126.         return null;
  127.         } catch (NoClassDefFoundError e) {
  128.         // We will print a message higher up, possibly before or earlier than
  129.         // in this invocation pass through applyDefinition.
  130.         return null;
  131.         }
  132.         // Check that the loaded class has the name we expect
  133.         if (!c.getName().equals(name)) {
  134.         System.err.println("\nWARNING: file name versus class name mismatch");
  135.         String fname = name.replace('.', '/') + ".class";
  136.         System.err.println("    JAR entry \"" + fname + "\" was expected "
  137.             + "to contain class \"" + name + "\"");
  138.         System.err.println("    but instead contained class \"" + c.getName() + "\"");
  139.         System.err.println("    This may cause future class-loading problems.\n");
  140.         }
  141.         return c;
  142.     }
  143.     }
  144.  
  145.     /**
  146.      * Define a class from a file
  147.      */
  148.  
  149.     private static byte[] getByteArray(String fileName) throws IOException {
  150.     File f = new File(fileName);
  151.     int length = (int)f.length();
  152.     byte buff[] = new byte[length];
  153.  
  154.     InputStream is = new FileInputStream(fileName);
  155.     int read = 0;
  156.     while (read < length) {
  157.         int r = is.read(buff, read, length-read);
  158.         if (r < 0) {
  159.         break;
  160.         }
  161.         read += r;
  162.     }
  163.     return buff;
  164.     }
  165.  
  166.     /**
  167.      * Helper function; load a class from a file
  168.      */
  169.     public Class loadClassFromFile(String fileName) throws ClassNotFoundException {
  170.     InputStream is;
  171.     try {
  172.         byte buf[] = getByteArray(fileName);
  173.         Class c = super.defineClass(null, buf, 0, buf.length);
  174.         if (c != null) {
  175.         resolveClass(c);
  176.         }
  177.         if (c==null)
  178.           throw new ClassNotFoundException(fileName);
  179.         return c;
  180.     } catch (Exception ex) {
  181.         debug("LoadFromFile/caught "+ex+" when loading from file "+fileName);
  182.         throw new ClassNotFoundException(fileName);
  183.     }
  184.     }
  185.  
  186.     /**
  187.      * Load a class from this class loader.
  188.      *
  189.      * @exception  ClassNotFoundException  if the class could not be found.
  190.      */
  191.     public Class loadClass(String name) throws ClassNotFoundException {
  192.     return loadClass(name, true);
  193.     }
  194.  
  195.     /**
  196.      * This is the main method for ClassLoaders, that is being redefined
  197.      *
  198.      * @exception  ClassNotFoundException  if the class could not be found.
  199.      */
  200.  
  201.     protected Class loadClass(String name, boolean resolve)
  202.         throws ClassNotFoundException {
  203.     
  204.     /* We check to see if we've already loaded it (i.e. in the cache) */
  205.     Class cl = findLoadedClass(name);
  206.  
  207.     // NOTE! the ordering is currently inverted from that in AppletClassLoader
  208.  
  209.     // If the class hasn't already been loaded from the JAR,
  210.     // we first try looking in the JAR:
  211.     if (cl == null) {
  212.         // First try to load the class from the JAR:
  213.         cl = applyDefinition(name, resolve);
  214.     }
  215.  
  216.     // If the class isn't in the JAR, try the system classloader:
  217.     if (cl == null) {
  218.         try {
  219.         cl = findSystemClass(name);
  220.         return cl;
  221.         } catch (ClassNotFoundException e) {
  222.         // Drop through.
  223.         }
  224.     }
  225.     if (cl == null) {
  226.         throw new ClassNotFoundException(name);
  227.     }
  228.     if (resolve) {
  229.         resolveClass(cl);
  230.     }
  231.     return cl;
  232.     }
  233.  
  234.     /**
  235.      * Interface to Beans.instantiate.
  236.      * Name is that of the bean.
  237.      * The stream producer provides the serialized representation of the bean
  238.      */
  239.  
  240.     public Object instantiate(String name, InputStreamProducer isp) 
  241.     throws ClassNotFoundException, IOException {
  242.     setLocalResourceSource(name+".ser", isp);
  243.     Object back = java.beans.Beans.instantiate(this, name);
  244.     localOverrides.remove(name);
  245.     return back;
  246.     }
  247.  
  248.     /**
  249.      * The resource stuff
  250.      */
  251.  
  252.     /**
  253.      * Assign an InputStream as the source for a given property name
  254.      * This value comes first after the system resources
  255.      */
  256.  
  257.     // This method can be private
  258.     public void setLocalResourceSource(String name, InputStreamProducer isp) {
  259.     localOverrides.put(name, isp);
  260.     }
  261.  
  262.     void putClassResource(String name, String type) {
  263.     resourceHash.put(name, "A CLASS FILE");
  264.     mimeHash.put(name, type);
  265.     }
  266.  
  267.     void putLocalResource(String name, byte[] data, String type) {
  268.     resourceHash.put(name, data);
  269.     mimeHash.put(name, type);
  270.     }
  271.  
  272.     public URL getResource(String name) {
  273.     URL back = getSystemResource(name);
  274.     if (back != null) {
  275.         return back;
  276.     }
  277.     return getLocalResource(name);
  278.     }
  279.  
  280.     public InputStream getResourceAsStream(String name) {
  281.     InputStream back = getSystemResourceAsStream(name);
  282.     if (back != null) {
  283.         return back;
  284.     }
  285.     return getLocalResourceAsStream(name);
  286.     }
  287.  
  288.  
  289.     /**
  290.      * Return a URL to the desired resource.
  291.      */
  292.     private URL getLocalResource(String name) {
  293.     // Check if there is an override
  294.     Object o;
  295.     o = localOverrides.get(name);
  296.     if (o == null) {
  297.         // Check on the JAR objects
  298.         o = resourceHash.get(name);
  299.     }
  300.     if (o == null && localResourceDirectory != null) {
  301.         // Try in localResourceDirectory
  302.         File f = new File(localResourceDirectory, name);
  303.         if (f.exists()) {
  304.         o = new Integer("1"); // anything will do
  305.         }
  306.     }
  307.     if (o != null) {
  308.         // Create a URL to refer to this resource
  309.         try {
  310.         URL url = new URL("simpleresource",
  311.                   "",
  312.                   "/"+urlPrefix+
  313.                   cookie+"/+/"+name);
  314.         return url;
  315.         } catch (Exception e) {
  316.         debug("Exception "+e+" while building a resource URL");
  317.         return null;
  318.         }
  319.     }
  320.     return null;
  321.     }
  322.  
  323.     private InputStream getLocalResourceAsStream(String name) {
  324.     Object o;
  325.     // Check if there is an override
  326.     o = localOverrides.get(name);
  327.     if (o!=null) {
  328.         return ((InputStreamProducer) o).getInputStream();
  329.     }
  330.     // Check data loaded from JAR
  331.     o = resourceHash.get(name);
  332.  
  333.     if (o != null) {
  334.         if (o instanceof String) {
  335.         // This is a .class entry...
  336.         // no access to .class files through getResource() in 1.1
  337.         throw new SecurityException("No access through getResource() to .class in 1.1");
  338.         }
  339.  
  340.         byte[] buf = (byte []) o;
  341.         return new ByteArrayInputStream(buf);
  342.     }
  343.     if (localResourceDirectory != null) {
  344.         // Now try in localResourceDirectory
  345.         File f = new File(localResourceDirectory, name);
  346.         try {
  347.         return new FileInputStream(f);
  348.         } catch (Exception ex) {
  349.         return null;
  350.         }
  351.     }
  352.     return null;
  353.     }
  354.  
  355.     /**
  356.      * Returns an InputStream on the resource
  357.      */
  358.  
  359.     public static SimpleClassLoader createLoader(String cookie,
  360.                          String dir) {
  361.     SimpleClassLoader back = getLoader(cookie);
  362.     if (back != null) {
  363.         if (!back.localResourceDirectory.equals(dir)) {
  364.         throw new Error("internal error!");
  365.         }
  366.         return back;
  367.     } else {
  368.         return new SimpleClassLoader(cookie, dir);
  369.     }
  370.     }
  371.  
  372.     private static SimpleClassLoader getLoader(String cookie) {
  373.     return (SimpleClassLoader) loaders.get(cookie);
  374.     }
  375.  
  376.     // get the local resource object...
  377.  
  378.     public static Object getLocalResource(String cookie,
  379.                       String name) {
  380.     SimpleClassLoader cl = getLoader(cookie);
  381.  
  382.     // Check if there is an override
  383.     Object o = cl.localOverrides.get(name);
  384.     if (o!=null) {
  385.         return ((InputStreamProducer) o).getInputStream();
  386.     }
  387.     // Check data loaded from JAR
  388.     String type = (String) cl.mimeHash.get(name);
  389.     if (type!=null) {
  390.         // Came from JAR
  391.         o = cl.resourceHash.get(name);
  392.  
  393.         if (o instanceof String) {
  394.         // This is a .class entry...
  395.         // no access to .class files through getResource() in 1.1
  396.         throw new SecurityException("No access through getResource() to .class in 1.1");
  397.         }
  398.  
  399.         byte[] buf = (byte []) o;
  400.         if (type.startsWith("image")) {
  401.         return new ByteArrayImageSource(buf);
  402.         } else {
  403.         return new ByteArrayInputStream(buf);
  404.         }
  405.     }
  406.     if (cl.localResourceDirectory != null) {
  407.         // Check into localResourceDirectory
  408.         File f = new File(cl.localResourceDirectory, name);
  409.         if (f.exists()) {
  410.         try {
  411.             URL url = new URL("file",
  412.                       "",
  413.                       f.getAbsolutePath());
  414.             return url.getContent();
  415.         } catch (Exception e) {
  416.             throw new Error("no such resource"); // should it be a security error?
  417.         }
  418.         }
  419.     }
  420.     return null;
  421.     }
  422.  
  423.     // REMIND - simplify the whole cookie thing.
  424.  
  425.     public static InputStream getLocalResourceAsStream(String cookie,
  426.                                String name) {
  427.     SimpleClassLoader cl = getLoader(cookie);
  428.     return cl.getLocalResourceAsStream(name);
  429.     }
  430.  
  431.     /**
  432.      * Define a class from the bytecodes that were collected early...
  433.      * Will return false if there were problems applying the definitions
  434.      *
  435.      * This is only invoked if we are defining classes up front.
  436.      * Code will be removed if we don't discover any problems in testing.
  437.      */
  438.  
  439.     public synchronized boolean applyDefinitions(Vector classList) {
  440.     // go through the bytecode arrays defininng.
  441.     // NOTE: sometimes a forward reference forces a class
  442.     // to get defined early; in that case, skip it!
  443.  
  444.     boolean back = true;
  445.     for (Enumeration k = classList.elements();
  446.          k.hasMoreElements();) {
  447.         String classname = (String) k.nextElement();
  448.         Class c = findLoadedClass(classname);
  449.         if (c == null) {
  450.         // Not yet defined, do so.
  451.         c = applyDefinition(classname, true);
  452.         if (c == null) {
  453.             if (back == true) {
  454.             System.err.println("NOTE: There are classes that cannot be defined in this JAR file");
  455.             System.err.println("    Some of these classes will cause the failure of defining or linking ");
  456.             System.err.println("    other classes that depend on them.");
  457.             if (keepLoading) {
  458.                 System.err.println("NOTE: To simplify debugging JAR files, we will proceed loading classes");
  459.                 System.err.println("    although this may lead eventually to an UnknownError or the like");
  460.                 System.err.println();
  461.             }
  462.             }
  463.             System.err.println("Class "+classname+" could not be defined from JAR file");
  464.             back = false;
  465.         }
  466.         }
  467.     }
  468.     return back;
  469.     }
  470.  
  471.     /**
  472.      * Debugging stuff
  473.      */
  474.     private static void debug(String msg) {
  475.     if (debug) {
  476.         System.err.println("SimpleClassLoader:: "+msg);
  477.     }
  478.     }
  479.  
  480.     /**
  481.      * Initialization of statics
  482.      */
  483.     static {
  484.     // Add this protocol type to the http properties
  485.     Properties newP = new Properties(System.getProperties());
  486.     newP.put(protocolPathProp,
  487.          newP.getProperty(protocolPathProp)+"|sun.beanbox");
  488.     System.setProperties(newP);
  489.     // *The* loader instance
  490.     ourLoader = createLoader("BeanBox", null);
  491.     }
  492. }
  493.