KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensugar > cube > simple > SimpleBundleClassLoader


1 /*
2  * JEFFREE: Java(TM) Embedded Framework FREE
3  * Copyright (C) 1999-2003 - Opensugar
4  *
5  * The contents of this file are subject to the Jeffree Public License,
6  * as defined by the file JEFFREE_LICENSE.TXT
7  *
8  * You may not use this file except in compliance with the License.
9  * You may obtain a copy of the License on the Objectweb web site
10  * (www.objectweb.org).
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
14  * the specific terms governing rights and limitations under the License.
15  *
16  * The Original Code is JEFFREE, including the java package com.opensugar.cube,
17  * released January 1, 2003.
18  *
19  * The Initial Developer of the Original Code is Opensugar.
20  * The Original Code is Copyright Opensugar.
21  * All Rights Reserved.
22  *
23  * Initial developer(s): Pierre Scokaert (Opensugar)
24  * Contributor(s):
25  */

26
27 package com.opensugar.cube.simple;
28
29 import com.opensugar.cube.BundleImpl;
30 import com.opensugar.cube.BundleClassLoader;
31 import com.opensugar.cube.Util;
32 import com.opensugar.cube.NamedPropertySet;
33 import com.opensugar.cube.java2.FileHelper;
34
35 import org.osgi.framework.FrameworkEvent;
36
37 import java.io.File JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.io.IOException JavaDoc;
40 import java.io.FileNotFoundException JavaDoc;
41 import java.io.FileOutputStream JavaDoc;
42 import java.io.FileInputStream JavaDoc;
43 import java.io.DataInputStream JavaDoc;
44 import java.net.URL JavaDoc;
45 import java.net.MalformedURLException JavaDoc;
46 import java.util.Hashtable JavaDoc;
47 import java.util.Enumeration JavaDoc;
48 import java.util.Vector JavaDoc;
49 import java.util.zip.ZipFile JavaDoc;
50 import java.util.zip.ZipEntry JavaDoc;
51
52 public class SimpleBundleClassLoader extends ClassLoader JavaDoc implements BundleClassLoader {
53
54    private BundleImpl bundle;
55    private ZipFile JavaDoc[] classPathJarFiles; // list of jar files on the class path in the order that they must be searched to find class
56
private Hashtable JavaDoc nativeLibraries; // library name --> file path (local copy of native library file)
57
private Hashtable JavaDoc classCache; // cache for loader classes: class name --> class
58
private Hashtable JavaDoc resourceURLCache; // cache for loader resource urls: resource name --> url
59

60    private ZipFile JavaDoc mainArchive; // should not be used except in clean-up
61

62    protected static BundleClassLoader createClassLoader( BundleImpl bundle, File JavaDoc bundleJarFile, NamedPropertySet[] nativeLibs, NamedPropertySet[] classPath ) throws IOException JavaDoc {
63       if ( isClassLoaderJava2() ) {
64          return new SecureBundleClassLoader( bundle, bundleJarFile, nativeLibs, classPath );
65       }
66       else {
67          return new SimpleBundleClassLoader( bundle, bundleJarFile, nativeLibs, classPath );
68       }
69    }
70
71    private static boolean isClassLoaderJava2() {
72       if ( System.getProperty( "com.opensugar.cube.classloader.type", "dynamic" ).equals( "basic" ) ) {
73          return false;
74       }
75       else {
76          return true;
77       }
78    }
79
80    // use this constructor for java 1
81
// in that case, cube0.jar should be on the classpath when the cube is started
82
private SimpleBundleClassLoader( BundleImpl bundle, File JavaDoc bundleJarFile, NamedPropertySet[] nativeLibs, NamedPropertySet[] classPath ) throws IOException JavaDoc {
83       super();
84
85       initialize( bundle, bundleJarFile, nativeLibs, classPath );
86    }
87
88    // can use this constructor for java 2
89
// in that case, cube0.jar should NOT be on the classpath when the cube is started
90
protected SimpleBundleClassLoader( BundleImpl bundle, File JavaDoc bundleJarFile, NamedPropertySet[] nativeLibs, NamedPropertySet[] classPath, ClassLoader JavaDoc parent ) throws IOException JavaDoc {
91       super( parent );
92
93       initialize( bundle, bundleJarFile, nativeLibs, classPath );
94    }
95
96    private void initialize( BundleImpl bundle, File JavaDoc bundleJarFile, NamedPropertySet[] nativeLibs, NamedPropertySet[] classPath ) throws IOException JavaDoc {
97       this.bundle = bundle;
98
99       mainArchive = new ZipFile JavaDoc( bundleJarFile );
100
101       Vector JavaDoc classPathFilesVector = new Vector JavaDoc();
102       if ( classPath == null || classPath.length == 0 ) {
103          // If class path is null or empty, include only bundle jar file in this class loader's archives
104
classPathFilesVector.addElement( bundleJarFile );
105       }
106       else {
107          // Add jar files in the order specified in the bundle class path manifest
108
// header
109
String JavaDoc classPathItem;
110          ZipEntry JavaDoc entry;
111          File JavaDoc innerJarFileDir;
112          String JavaDoc innerJarFileName;
113          File JavaDoc innerJarFile;
114          int n;
115          for ( int i = 0; i < classPath.length; i++ ) {
116             classPathItem = classPath[ i ].getName().trim();
117             if ( classPathItem.equals( "." ) ) {
118                // If class path item is ".", then add the bundle jar to the class path
119
classPathFilesVector.addElement( bundleJarFile );
120             }
121             else {
122                // If class path item is not ".", it must be the name of an inner jar file
123
// contained in the bundle jar file.
124
// Add the inner jar file to the class path
125
entry = mainArchive.getEntry( classPathItem );
126                if ( entry != null ) {
127                   innerJarFileDir = bundle.getCube().getBundleInnerFilesDirectory( bundle.getBundleId() );
128                   innerJarFileName = classPathItem;
129                   n = innerJarFileName.indexOf( '/' );
130                   while ( n > 0 && n < innerJarFileName.length() ) {
131                      innerJarFileDir = new File JavaDoc( innerJarFileDir, innerJarFileName.substring( 0, n ) );
132                      innerJarFileDir.mkdir();
133                      innerJarFileName = innerJarFileName.substring( n + 1 );
134                      n = innerJarFileName.indexOf( '/' );
135                   }
136                   innerJarFile = new File JavaDoc( innerJarFileDir, innerJarFileName );
137                   Util.transferData( mainArchive.getInputStream( entry ), new FileOutputStream JavaDoc( innerJarFile ) );
138                   classPathFilesVector.addElement( innerJarFile );
139                }
140                else {
141                   // ignore missing inner jar file
142
//throw new FileNotFoundException( "Missing inner jar in bundle jar: " + classPathItem );
143
}
144             }
145          }
146       }
147       classPathJarFiles = new ZipFile JavaDoc[ classPathFilesVector.size() ];
148       for ( int i = 0; i < classPathFilesVector.size(); i++ ) {
149          classPathJarFiles[ i ] = new ZipFile JavaDoc( (File JavaDoc)classPathFilesVector.elementAt( i ) );
150       }
151
152       nativeLibraries = new Hashtable JavaDoc();
153       if ( nativeLibs != null ) {
154          String JavaDoc nativeLibName;
155          ZipEntry JavaDoc entry;
156          File JavaDoc innerFileDir;
157          File JavaDoc innerFile;
158          int n;
159          for ( int i = 0; i < nativeLibs.length; i++ ) {
160             nativeLibName = nativeLibs[ i ].getName();
161             entry = mainArchive.getEntry( nativeLibName );
162             if ( entry != null ) {
163                innerFileDir = bundle.getCube().getBundleInnerFilesDirectory( bundle.getBundleId() );
164                //innerFileName = nativeLibName;
165
n = nativeLibName.indexOf( '/' );
166                while ( n > 0 && n < nativeLibName.length() ) {
167                   innerFileDir = new File JavaDoc( innerFileDir, nativeLibName.substring( 0, n ) );
168                   innerFileDir.mkdir();
169                   nativeLibName = nativeLibName.substring( n + 1 );
170                   n = nativeLibName.indexOf( '/' );
171                }
172                innerFile = new File JavaDoc( innerFileDir, nativeLibName );
173                Util.transferData( mainArchive.getInputStream( entry ), new FileOutputStream JavaDoc( innerFile ) );
174                String JavaDoc nativeLibPath = innerFile.getAbsolutePath();
175                registerNativeLibrary( nativeLibName, nativeLibPath );
176             }
177             else {
178                // ignore missing native library
179
//throw new FileNotFoundException( "Missing native library in bundle jar: " + nativeLibName );
180
}
181          }
182       }
183
184       classCache = new Hashtable JavaDoc();
185       resourceURLCache = new Hashtable JavaDoc();
186    }
187
188    private void registerNativeLibrary( String JavaDoc nativeLibName, String JavaDoc nativeLibPath ) {
189       // register library with library name
190
nativeLibraries.put( nativeLibName, nativeLibPath );
191       // register library with library name without extensions
192
int index = nativeLibName.indexOf( "." );
193       if ( index != -1 ) {
194          nativeLibraries.put( nativeLibName.substring( 0, index ), nativeLibPath );
195       }
196
197       // register library without directory info
198
index = nativeLibName.lastIndexOf( "/" );
199       if ( index != -1 ) {
200          nativeLibraries.put( nativeLibName.substring( index + 1 ), nativeLibPath );
201       }
202
203       // register library without lib at beginning
204
if ( nativeLibName.startsWith( "lib" ) && nativeLibName.length() > 3 ) {
205          nativeLibraries.put( nativeLibName.substring( 3 ), nativeLibPath );
206       }
207
208       // copy native library to library path directory
209
String JavaDoc nativeLibraryDir = System.getProperty( "com.opensugar.cube.library.dir", "nativeLibs" );
210       File JavaDoc dir = new File JavaDoc( nativeLibraryDir );
211       if ( !dir.exists() ) {
212          dir.mkdirs();
213       }
214       try {
215          Util.transferData( new FileInputStream JavaDoc( new File JavaDoc( nativeLibPath ) ), new FileOutputStream JavaDoc( new File JavaDoc( dir, nativeLibName ) ) );
216       }
217       catch( IOException JavaDoc e ) {
218          bundle.getCube().log( bundle.getCube().LOG_ERROR, "Cannot create extract native library file from bundle jar to disk", e );
219       }
220    }
221
222    // Library loading in JDK 1.2
223
public String JavaDoc findLibrary( String JavaDoc name ) {
224       return (String JavaDoc)nativeLibraries.get( name );
225    }
226
227    public Class JavaDoc loadClass( String JavaDoc name ) throws ClassNotFoundException JavaDoc {
228       return loadClass( name, true );
229    }
230
231    protected Class JavaDoc loadClass( String JavaDoc name, boolean resolve ) throws ClassNotFoundException JavaDoc {
232       name = processClassName( name );
233
234       Class JavaDoc clazz = null;
235
236       // First, try to load class from system class loader
237
try {
238          if ( isClassLoaderJava2() ) {
239             clazz = getParent().loadClass( name );
240          }
241          else {
242             clazz = findSystemClass( name );
243          }
244       }
245       catch( ClassNotFoundException JavaDoc ignore ) {}
246
247       // If class not found by system class loader, try to load class from imported
248
// package (after checking caller has appropriate IMPORT PackagePermission)
249
if ( clazz == null ) {
250          int n = name.lastIndexOf( "." );
251          if ( n != -1 ) {
252             // no need to check package permission
253
// BundleImpl.initializeBundle() is where package exports and imports are
254
// initialized, and a package import is only allowed if required package
255
// permission exists
256
try {
257                clazz = bundle.getCube().getPackageAdmin().loadClass( bundle, name );
258             }
259             catch ( ClassNotFoundException JavaDoc ignore ) {}
260          }
261       }
262
263       // If class not found either by system class loader or from imported package
264
// load class from the class path of the bundle that owns this class loader
265
if ( clazz == null ) {
266          clazz = loadOwnClass( name );
267       }
268
269       return clazz;
270    }
271
272    public URL JavaDoc getResource( String JavaDoc name ) {
273       name = processResourceName( name );
274
275       URL JavaDoc url = null;
276
277       // First, try to get resource from system class loader
278
if ( isClassLoaderJava2() ) {
279          url = getParent().getResource( name );
280       }
281       else {
282          url = super.getResource( name );
283       }
284
285       // If resource not found by system class loader, try to get resource from imported
286
// package (after checking caller has appropriate IMPORT PackagePermission)
287
if ( url == null ) {
288          int n = name.lastIndexOf( "/" );
289          if ( n != -1 ) {
290             // no need to check package permission
291
// BundleImpl.initializeBundle() is where package exports and imports are
292
// initialized, and a package import is only allowed if required package
293
// permission exists
294
url = bundle.getCube().getPackageAdmin().getResource( bundle, name );
295          }
296       }
297
298       // If resource not found either by system class loader or from imported package
299
// get resource from the class path of the bundle that owns this class loader
300
if ( url == null ) {
301          url = getOwnResource( name );
302       }
303
304       return url;
305    }
306
307    public InputStream JavaDoc getResourceAsStream( String JavaDoc name ) {
308       URL JavaDoc url = getResource( name );
309       if ( url == null ) {
310          return null;
311       }
312       else {
313          try {
314             if ( url.toString().startsWith( "file://" ) ) {
315                String JavaDoc filename = url.toString();
316                filename = filename.substring( 7, filename.length() );
317                return new FileInputStream JavaDoc( filename );
318             }
319             return url.openStream();
320          }
321          catch( IOException JavaDoc e ) {
322             return null;
323          }
324       }
325    }
326
327    private String JavaDoc processClassName( String JavaDoc name ) {
328       // Sun VMs call this with '.' separators in the class name,
329
// but Perc VM calls this method with '/' separators
330
return name.replace( '/', '.' );
331    }
332
333    private String JavaDoc processResourceName( String JavaDoc name ) {
334       // Sun VMs seem to make no difference whether or not the name
335
// starts with "/",
336
// but Perc does not find the jar file entry if its name starts with "/".
337
// So, we strip the starting "/" if there is one.
338
if ( name.startsWith( "/" ) && name.length() > 1 ) {
339          name = name.substring( 1, name.length() );
340       }
341
342       return name;
343    }
344
345    public Class JavaDoc loadOwnClass( String JavaDoc name ) throws ClassNotFoundException JavaDoc {
346       Class JavaDoc clazz = (Class JavaDoc)classCache.get( name );
347       if ( clazz == null ) {
348          clazz = defineClass( name );
349          classCache.put( name, clazz );
350       }
351       return clazz;
352    }
353
354    public URL JavaDoc getOwnResource( String JavaDoc name ) {
355       URL JavaDoc url = (URL JavaDoc)resourceURLCache.get( name );
356       if ( url == null ) {
357          url = defineResourceURL( name );
358          if ( url != null ) {
359             resourceURLCache.put( name, url );
360          }
361       }
362       return url;
363    }
364
365    public void cleanUp() throws IOException JavaDoc {
366       for ( int i = 0; i < classPathJarFiles.length; i++ ) {
367          if ( !classPathJarFiles[ i ].equals( mainArchive ) ) {
368             classPathJarFiles[ i ].close();
369          }
370       }
371       mainArchive.close();
372
373       classPathJarFiles = null;
374    }
375
376    public Object JavaDoc getCodeSource() {
377       return null;
378    }
379
380    private Class JavaDoc defineClass( String JavaDoc name ) throws ClassNotFoundException JavaDoc {
381       String JavaDoc classFilePath = name.replace( '.', '/' ) + ".class";
382
383       byte[] bytes;
384       // search for class in class path jar files, in the order specified by the
385
// bundle's class path manifest header value
386
for ( int i = 0; i < classPathJarFiles.length; i++ ) {
387          try {
388             bytes = getJarEntryContents( classPathJarFiles[ i ], classFilePath );
389             if ( bytes != null ) {
390                Class JavaDoc clazz = defineClass( name, bytes, 0, bytes.length );
391                return clazz;
392             }
393          }
394          catch( IOException JavaDoc e ) {
395             bundle.getCube().fireFrameworkEvent( FrameworkEvent.ERROR, bundle, e );
396          }
397       }
398       throw new ClassNotFoundException JavaDoc( name );
399    }
400
401    private URL JavaDoc defineResourceURL( String JavaDoc name ) {
402       // search for resource in class path jar files, in the order specified by the
403
// bundle's class path manifest header value
404
for ( int i = 0; i < classPathJarFiles.length; i++ ) {
405          URL JavaDoc url = getJarEntryURL( classPathJarFiles[ i ], name );
406          if ( url != null ) {
407             return url;
408          }
409       }
410       return null;
411    }
412
413    // Get a url reference for a named entry inside one of this class loader's archives.
414
private static int unjarCount = 0;
415    private static Hashtable JavaDoc unjarred = new Hashtable JavaDoc();
416    private URL JavaDoc getJarEntryURL( ZipFile JavaDoc archive, String JavaDoc entryName ) {
417       if ( archive.getEntry( entryName ) != null ) {
418          try {
419             // This causes a MalformedURLException
420
//url = new URL( "jar:" + FileHelper.toURL( file ) + "!/" + entryName );
421

422             // I don't know how to solve the problem, so unjar the file and return
423
// the url of the uncompressed file
424
String JavaDoc key = archive.getName() + "." + entryName;
425             File JavaDoc tmp;
426             if ( unjarred.get( key ) == null ) {
427                tmp = new File JavaDoc( getUnjarDirectory(), "unjar" + unjarCount++ );
428                FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc( tmp );
429                fos.write( getJarEntryContents( archive, entryName ) );
430                fos.close();
431                unjarred.put( key, tmp );
432             }
433             tmp = (File JavaDoc)unjarred.get( key );
434             return FileHelper.toURL( tmp );
435          }
436          catch( MalformedURLException JavaDoc e ) {
437             bundle.getCube().log( bundle.getCube().LOG_ERROR, "Error getting jar entry url", e );
438          }
439          catch( IOException JavaDoc e ) {
440             bundle.getCube().log( bundle.getCube().LOG_ERROR, "Error getting jar entry url", e );
441          }
442       }
443       return null;
444    }
445
446    private File JavaDoc unjarDirectory;
447    private synchronized File JavaDoc getUnjarDirectory() {
448       if ( unjarDirectory == null ) {
449          // this is the first time we get the directory
450
// create the directory
451
unjarDirectory = bundle.getBundleContext().getDataFile( "temp/unjar" );
452          if ( unjarDirectory.exists() ) {
453             // clean up old stuff from previous engine runs
454
Util.recursiveFileDelete( unjarDirectory );
455          }
456          // some virtual machines seem to have a problem recursively creating directories
457
// (e.g. personal java 3.1 for windows from sun). Give them some help
458
unjarDirectory = bundle.getBundleContext().getDataFile( "temp" );
459          unjarDirectory.mkdirs();
460          unjarDirectory = bundle.getBundleContext().getDataFile( "temp/unjar" );
461          unjarDirectory.mkdirs();
462       }
463       return unjarDirectory;
464    }
465
466    // Get a byte array containing the contents of a named entry from one of this class
467
// loader's archives.
468
private byte[] getJarEntryContents( ZipFile JavaDoc archive, String JavaDoc entryName ) throws IOException JavaDoc {
469       ZipEntry JavaDoc entry = archive.getEntry( entryName );
470       if ( entry != null ) {
471          int entrySize = (int)entry.getSize();
472          if ( entrySize < 0 ) {
473             throw new IOException JavaDoc( "Unknown size for " + entry );
474          }
475          InputStream JavaDoc is = archive.getInputStream( entry );
476          byte[] bytes = new byte[ entrySize ];
477          DataInputStream JavaDoc dis = new DataInputStream JavaDoc( is );
478          dis.readFully( bytes );
479          is.close();
480          return bytes;
481       }
482       return null;
483    }
484
485 }
486
487
Popular Tags