KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > mobilitools > smi > lib > SMIClassLoader


1 /*
2 * MobiliTools: an implementation of the Object Management Group's
3 * Mobile Agent Facility specification.
4 * Copyright (C) 2003 France Telecom R&D
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * MobiliTools $Name: $
21 *
22 * Contact: mobilitools-smi@lists.debian-sf.objectweb.org
23 *
24 * Authors: Bruno Dillenseger
25 */

26
27
28 package org.objectweb.mobilitools.smi.lib;
29
30
31 import org.objectweb.mobilitools.smi.lib.bytearray.ByteArrayURLStreamHandler;
32 import org.omg.CfMAF.*;
33 import org.omg.CORBA.*;
34 import java.net.*;
35 import java.io.*;
36 import java.util.*;
37 import java.util.jar.*;
38
39
40 /**
41  * MobiliTools $Name: $, $Id: SMIClassLoader.java,v 1.1.1.1 2003/03/28 14:48:06 dillense Exp $
42  * <P>
43  * MAF-based class loader for SMI. Makes it possible to define classes by
44  * loading their byte code from a local filesystem, a URL, or an implementation
45  * of MAF's MAFAgentSystem interface. It also handles access to resources in
46  * the same manner. A dedicated classloader is created for each new mobile
47  * object (created or incoming). However, byte-code, classes and resources are
48  * kept in a cache and reused for each given codebase. This way, mobile objects
49  * may invoke each other through and only through a class, interface or object
50  * with the same codebase, or loaded by a common parent class loader.
51  * <P>
52  * The class/resource loading policy is the following:
53  * <OL>
54  * <LI>try to get the missing class/resource from the parent classloader
55  * <LI>try to retrieve the missing class/resource from the local cache
56  * <LI>ask the class provider (SMI agency), which, in turn, looks for the
57  * class byte-code or resource in its cache
58  * <LI>locally interpret the codebase
59  * </OL>
60  * A cache is flushed at garbage collection time if and only if no more agent
61  * with the corresponding codebase is present in the agency.
62  * <P>
63  * Notes:
64  * <UL>
65  * <LI>SMIClassLoader typically makes it possible to move agents even if
66  * their original class source (HTTP server, local file or whatever) is not
67  * reachable any more.
68  * <LI>if you wish to use a simple URL class loading behaviour, you may
69  * look at URLClassLoaderAdapter class
70  * <LI>if mobile objects use RMI, RMIClassLoaders should be used (see
71  * RMIClassLoaderAdapter class)
72  * <LI>to use custom classloaders, declare the corresponding factory in the
73  * appropriate system property. This classloader factory class must
74  * implement a static public method getClassLoader() returning the custom
75  * class loaders.
76  * </UL>
77  * @see RMIClassLoaderAdapter
78  * @see URLClassLoaderAdapter
79  * @see #getClassLoader(ClassLoader, String, AgentProfile, MAFAgentSystem)
80  * @see org.objectweb.mobilitools.smi.api.Constants
81 */

82 public class SMIClassLoader extends ClassLoader JavaDoc
83 {
84     String JavaDoc my_codebase;
85     AgentProfile my_profile;
86     MAFAgentSystem my_provider;
87     volatile ClassCache my_cache;
88
89
90     /**
91      * Creates a new SMI class loader.
92      * @param the parent class loader (from which SMI class loader tries to get
93      * classes before fetching them by itself)
94      * @param codebase the codebase for the classes; may be a single pathname or
95      * URL designating a ZIP/JAR file or directory holding the classes
96      * @param profile the agent profile (based on SMI's profile, see MAF spec.)
97      * @param provider the MAFAgentSystem implementation (SMI agency) providing
98      * the classes.
99      * @return each call returns a new classloader
100      */

101     static public ClassLoader JavaDoc getClassLoader(
102         ClassLoader JavaDoc parent,
103         String JavaDoc codebase,
104         AgentProfile profile,
105         MAFAgentSystem provider)
106     {
107         return new SMIClassLoader(parent, codebase, profile, provider);
108     }
109
110
111     /**
112         Reads or retrieves from a cache the byte code for a given class name, or
113         a resource content (accordingly to current class loader's code base).
114         There is one cache per represented codebase. Caches are filled by the findClass()
115         instance method, when a class is downloaded from another
116         agency by the fetch_class() operation.
117         @param name the class full name (i.e. including the package name if any) or resource name
118         @return the byte code or the resource content designated by the name, or
119         null if the byte code or resource could not be found.
120     */

121     public byte[] getBytes(String JavaDoc name)
122     {
123         synchronized (my_cache)
124         {
125             byte[] result = null;
126             // try to get the class code from the cache (in case it has already been downloaded for current codebase)
127
result = (byte[])my_cache.get(name);
128             if (result == null && my_provider != null)
129             {
130                 // invoke a MAFAgentSystem to get the bytes
131
try
132                 {
133                     ClassName[] className = { new ClassName(name, new byte[0]) };
134                     result = my_provider.fetch_class(className, my_codebase, my_profile)[0];
135                 }
136                 catch (Exception JavaDoc ex)
137                 {
138                     System.err.println("warning: could not get bytes from class provider (" + ex + ") => trying codebase interpretation");
139                 }
140             }
141             if (result == null)
142             {
143                 // get the class code from a JAR or class file, designated by a local file path, or a URL
144
try
145                 {
146                     // is the codebase a URL?
147
URL url = null;
148                     try
149                     {
150                         url = new URL(my_codebase);
151                     }
152                     catch (MalformedURLException e)
153                     {
154                     }
155                     InputStream is = null;
156                     boolean is_jar = false;
157                     JarInputStream jis = null;
158                     JarEntry je = null;
159                     if (url == null)
160                     // the codebase is local path
161
{
162                         if (my_codebase.endsWith("/"))
163                         // the codebase is a local directory (not a JAR file)
164
{
165                             is = new FileInputStream(my_codebase + name);
166                         }
167                         else
168                         // the codebase may be a JAR file
169
{
170                             try
171                             {
172                                 is = new FileInputStream(my_codebase);
173                                 jis = new JarInputStream(is);
174                                 je = jis.getNextJarEntry();
175                             }
176                             catch (FileNotFoundException e)
177                             {
178                             }
179                             if (je == null)
180                             // the codebase is not a JAR file (or an empty JAR we ignore)
181
{
182                                 is = new FileInputStream(my_codebase + "/" + name);
183                             }
184                             else
185                             // the codebase is a JAR file
186
{
187                                 is_jar = true;
188                             }
189                         }
190                     }
191                     else
192                     // the codebase is a URL
193
{
194                         if (my_codebase.endsWith("/"))
195                         // the codebase URL designates a directory
196
{
197                             url = new URL(url, name);
198                             is = url.openConnection().getInputStream();
199                         }
200                         else
201                         // the codebase URL may designate a JAR file
202
{
203                             try
204                             {
205                                 is = url.openConnection().getInputStream();
206                                 jis = new JarInputStream(is);
207                                 je = jis.getNextJarEntry();
208                             }
209                             catch (IOException e)
210                             {
211                             }
212                             if (je == null)
213                             // the codebase URL is not a JAR file (or an empty JAR we ignore)
214
{
215                                 url = new URL(my_codebase + "/" + name);
216                                 is = url.openConnection().getInputStream();
217                             }
218                             else
219                             // the codebase URL is a JAR file
220
{
221                                 is_jar = true;
222                             }
223                         }
224                     }
225                     // get an input stream from the byte code file or URL
226
BufferedInputStream bis = null;
227                     if (is_jar)
228                     // get the right entry from the JAR
229
{
230                         while (je != null && !je.getName().equals(name))
231                         {
232                             je = jis.getNextJarEntry();
233                         }
234                         if (je != null)
235                         {
236                             bis = new BufferedInputStream(jis);
237                         }
238                     }
239                     else
240                     {
241                         bis = new BufferedInputStream(is);
242                     }
243                     // load byte code and tranfer it to a byte array
244
ByteArrayOutputStream baos = new ByteArrayOutputStream();
245                     int n;
246                     while ((n = bis.read()) != -1)
247                     {
248                         baos.write(n);
249                     }
250                     result = baos.toByteArray();
251                 }
252                 catch (Exception JavaDoc e)
253                 {
254                 }
255             }
256             if (result != null)
257             {
258                 my_cache.put(name, result);
259             }
260             return result;
261         }
262     }
263
264
265     /**
266         Creates a new SMI class loader, able to load classes from the local filesystem, URLs,
267         and a MAFAgentSystem inplementation.
268         @param parent a classloader able to load classes from the classpath.
269         If null, the system class loader will be used for that purpose.
270         @param codebase the SMI class loader's codebase (single filesystem
271         pathname or URL designating a ZIP/JAR file or a directory)
272         @param profile the agent profile (see MAF specifications)
273         @param provider the remote MAFAgentSystem that may provide the missing classes.
274         Typically, the provider is the agency where a incoming agent is migrating from.
275     */

276     SMIClassLoader(ClassLoader JavaDoc parent, String JavaDoc codebase, AgentProfile profile, MAFAgentSystem provider)
277     {
278         super(parent);
279         my_codebase = codebase;
280         my_profile = profile;
281         my_provider = provider;
282         my_cache = ClassCache.getCache(codebase);
283         ClassCache.inc(codebase);
284     }
285
286
287     /**
288         Gets the byte code for, and defines the requested class, either through
289         local or remote bytecode load.
290         Byte-code remotely loaded through the fetch_class() operation of a MAFAgentSystem
291         implementation is cached. This
292         cache is necessary for the next migration of the incoming agent, in order to
293         provide the next hosting agency with the class bytecode. Classes in the cache are
294         identified by their name and codebase.
295         @param name the name of the class to define (see Java2 API).
296         @return the corresponding Class object.
297         @exception ClassNotFoundException if the class could not be loaded and defined.
298     */

299     protected Class JavaDoc findClass(String JavaDoc name)
300         throws ClassNotFoundException JavaDoc
301     {
302         synchronized (my_cache)
303         {
304             Class JavaDoc result = null;
305             String JavaDoc pathname = name.replace('.', '/') + ".class";
306             // first, see if the class can be found using the classpath
307
try
308             {
309                 ClassLoader JavaDoc parent = getParent();
310                 if (parent == null)
311                 {
312                     parent = getSystemClassLoader();
313                 }
314                 result = parent.loadClass(name);
315             }
316             catch (ClassNotFoundException JavaDoc e)
317             {
318                 // second, see if the class has already been defined for current codebase
319
result = (Class JavaDoc)my_cache.get(name);
320                 if (result == null)
321                 {
322                     // then, try to use the codebase and the provider
323
byte[] code = getBytes(pathname);
324                     if (code != null)
325                     {
326                         result = defineClass(name, code, 0, code.length);
327                         my_cache.put(name, result);
328                     }
329                     else
330                     {
331                         throw new ClassNotFoundException JavaDoc(name);
332                     }
333                 }
334             }
335             return result;
336         }
337     }
338
339
340     /**
341      * Gets the content of the named resource and keeps it in the cache
342      * associated to current SMI class loader's codebase.
343      * @param name the resource's name
344      * @return a URL with a specific scheme to access the resource content, or
345      * null if the resource could not be found/loaded.
346      */

347     public URL findResource(String JavaDoc name)
348     {
349         try
350         {
351             return new URL(null, "bytearray:" + my_codebase + "#" + name, new ByteArrayURLStreamHandler(this));
352         }
353         catch (Exception JavaDoc ex)
354         {
355             ex.printStackTrace();
356             return null;
357         }
358     }
359
360
361     /**
362      * Since a codebase for SMI class loaders hold a single value, this method
363      * is similar to findResource().
364      * @param name the resource's name
365      * @return an empty or single-value enumeration of URLs for the named
366      * resource
367      * @see #findResource(String)
368      */

369     public Enumeration findResources(String JavaDoc name)
370     {
371         Vector result = new Vector(1);
372         URL url = findResource(name);
373         if (url != null)
374         {
375             result.add(url);
376         }
377         return result.elements();
378     }
379
380
381     public String JavaDoc toString()
382     {
383         return "SMIClassLoader for codebase " + my_codebase;
384     }
385
386
387     public void finalize()
388     {
389         ClassCache.dec(my_codebase);
390     }
391 }
392
Popular Tags