001    /*
002      Copyright (C) 2001 Renaud Pawlak <renaud@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser Generaly Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
017    
018    package org.objectweb.jac.core.dist;
019    
020    import java.io.FileInputStream;
021    import java.util.Hashtable;
022    import org.apache.log4j.Logger;
023    
024    /**
025     * DistdClassLoader is a class loader that load classes from a remote
026     * JAC container (the class repository site).
027     *
028     * @see RemoteContainer#getByteCodeFor(String)
029     *
030     * @author <a href="http://cedric.cnam.fr/~pawlak/index-english.html">Renaud Pawlak</a> */
031    
032    public class DistdClassLoader extends ClassLoader {
033        static Logger logger = Logger.getLogger("dist.classloader");
034    
035       /** loadedClasses is a hashtable of classes that have been
036           loaded. */
037        protected transient Hashtable loadedClasses = new Hashtable();
038    
039        /** loadedByteCodes is a hashtable of byte codes that have been
040           remotely load (so that this site may be used as an intermediate
041           class repository. */
042        protected Hashtable loadedByteCodes = new Hashtable();
043    
044        /** Bootstrapping flag (do not load classes while true). */
045        public boolean bootstrapping = true;
046    
047        /** The class repository site. */
048        public static String classRepositoryName = null;
049       
050        /**
051        * Overrides the default mechanism to load classes (only for
052        * non-java classes). The behavior is the following :<p>
053        *
054        * <ul>
055        *   <li>check whether the class is already loaded</li>
056        *   <li>try to load it from the class repository site</li>
057        *   <li>if success, store its bytecode (see getByteCode)</li>
058        *   <li>try to load it from the local filesystem</li>
059        *   <li>if all these failed, delegate to the parent</li>
060        * </ul>
061        * 
062        * @param name the name for the class to load
063        * @return the loaded class
064        */
065    
066        public Class loadClass(String name)
067            throws ClassNotFoundException {
068    
069            /** Do not reload the same class */
070            if (name.equals(getClass().getName())) {
071                logger.debug("Do not reload "+getClass().getName());
072                return getClass();
073            }
074    
075            Class cl;
076            if (name.startsWith("java.")) {
077                logger.debug("Get system class "+name+" from parent classloader");
078                cl = getParent().loadClass(name);
079                loadedClasses.put(name, cl);
080                return cl;
081            }
082          
083            /** Check whether already loaded */
084            cl = (Class)loadedClasses.get(name);
085    
086            if (cl == null) {
087    
088                byte[] bc = null;
089    
090                /** Check if we know a class repository to download the bytecode */
091    
092                if ( (!bootstrapping) && classRepositoryName != null ) {
093    
094                    /** Download the bytecode */
095                    logger.debug("Downloading "+name+" from "+classRepositoryName);
096                    RemoteContainer rc = RemoteContainer.resolve(classRepositoryName);
097                    try {
098                        bc = rc.getByteCodeFor(name);
099                    } catch(Exception e) {
100                        logger.debug("Failed to get bytecode for "+name+
101                                  " from "+classRepositoryName+": "+e);
102                    }
103                    if (bc != null) {
104                        loadedByteCodes.put(name, bc);
105                    }
106                }
107             
108                /** Check if download was successfully performed */
109    
110                if (bc == null) {
111    
112                    /** Try to load it from the local filesystem */
113                    logger.debug("Loading "+name+" from local FS");
114                    try {
115                        FileInputStream in = new FileInputStream( 
116                            getParent().getResource(name.replace('.','/')+".class").getFile());
117                        bc = new byte[in.available()];
118                        in.read(bc);
119                    } catch (Exception e) {
120                        /** If failed, delegate to the parent classloader */
121                        cl = getParent().loadClass(name);
122                        loadedClasses.put(name, cl);
123                        return cl;
124                    }
125                
126                }
127    
128                if (bc != null) {
129                    /** Define the class from the bytecode */
130                    try {
131                        cl = defineClass(name, bc, 0, bc.length);
132                        loadedClasses.put(name, cl);
133                    } catch (Exception e) {
134                        /** Bytecode was corrupted or some error occured */
135                        logger.error("Cannot load class "+name,e);
136                    }
137                }
138          
139            } else {
140                logger.debug("Already loaded "+name);
141            }
142    
143            return cl;
144        }
145    
146        /**
147        * Gets the bytecode for a given remotely loaded class name.
148        *
149        * @param className the name of the class
150        * @return the corresponding bytecode */
151    
152        public byte[] getByteCode (String className) {
153            byte[] bc = (byte[])loadedByteCodes.get (className);
154            if (bc == null) {
155            }
156            return bc;
157        }
158          
159    }