KickJava   Java API By Example, From Geeks To Geeks.

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


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 import java.security.SecureClassLoader JavaDoc;
53 import java.security.CodeSource JavaDoc;
54
55 public class SecureBundleClassLoader extends SecureClassLoader JavaDoc implements BundleClassLoader {
56
57    private BundleImpl bundle;
58    private ZipFile JavaDoc[] classPathJarFiles; // list of jar files on the class path in the order that they must be searched to find class
59
private Hashtable JavaDoc nativeLibraries; // library name --> file path (local copy of native library file)
60
private Hashtable JavaDoc classCache; // cache for loader classes: class name --> class
61
private Hashtable JavaDoc resourceURLCache; // cache for loader resource urls: resource name --> url
62

63    private ZipFile JavaDoc mainArchive; // should not be used except in clean-up
64

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

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