KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > framework > internal > defaultadaptor > DefaultClassLoader


1 /*******************************************************************************
2  * Copyright (c) 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.osgi.framework.internal.defaultadaptor;
13
14 import java.io.*;
15 import java.net.URL JavaDoc;
16 import java.security.ProtectionDomain JavaDoc;
17 import java.util.*;
18 import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
19 import org.eclipse.osgi.framework.adaptor.core.*;
20 import org.eclipse.osgi.framework.debug.Debug;
21 import org.osgi.framework.BundleException;
22 import org.osgi.framework.FrameworkEvent;
23
24 /**
25  * A concrete implementation of BundleClassLoader. This implementation
26  * consolidates all Bundle-ClassPath entries into a single ClassLoader.
27  */

28 public class DefaultClassLoader extends AbstractClassLoader {
29     /**
30      * The BundleData object for this BundleClassLoader
31      */

32     protected AbstractBundleData hostdata;
33
34     /**
35      * The ClasspathEntries for this BundleClassLoader. Each ClasspathEntry object
36      * represents on Bundle-ClassPath entry.
37      */

38     protected ClasspathEntry[] classpathEntries;
39
40     protected Vector fragClasspaths; //TODO This should be an array or an arraylist if the synchronization is not required
41

42     /**
43      * The buffer size to use when loading classes. This value is used
44      * only if we cannot determine the size of the class we are loading.
45      */

46     protected int buffersize = 8 * 1024;
47
48     /**
49      * BundleClassLoader constructor.
50      * @param delegate The ClassLoaderDelegate for this ClassLoader.
51      * @param domain The ProtectionDomain for this ClassLoader.
52      * @param classpath An array of Bundle-ClassPath entries to
53      * use for loading classes and resources. This is specified by the
54      * Bundle-ClassPath manifest entry.
55      * @param parent The parent ClassLoader.
56      * @param bundledata The BundleData for this ClassLoader
57      */

58     public DefaultClassLoader(ClassLoaderDelegate delegate, ProtectionDomain JavaDoc domain, String JavaDoc[] classpath, ClassLoader JavaDoc parent, AbstractBundleData bundledata) {
59         super(delegate, domain, classpath, parent);
60         this.hostdata = bundledata;
61
62         try {
63             hostdata.open(); /* make sure the BundleData is open */
64         } catch (IOException e) {
65             hostdata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, hostdata.getBundle(), e);
66         }
67     }
68
69     public void initialize() {
70         classpathEntries = buildClasspath(hostclasspath, hostdata, hostdomain);
71     }
72
73     /**
74      * Attaches the BundleData for a fragment to this BundleClassLoader.
75      * The Fragment BundleData resources must be appended to the end of
76      * this BundleClassLoader's classpath. Fragment BundleData resources
77      * must be searched ordered by Bundle ID's.
78      * @param bundledata The BundleData of the fragment.
79      * @param domain The ProtectionDomain of the resources of the fragment.
80      * Any classes loaded from the fragment's BundleData must belong to this
81      * ProtectionDomain.
82      * @param classpath An array of Bundle-ClassPath entries to
83      * use for loading classes and resources. This is specified by the
84      * Bundle-ClassPath manifest entry of the fragment.
85      */

86     public void attachFragment(org.eclipse.osgi.framework.adaptor.BundleData bundledata, ProtectionDomain JavaDoc domain, String JavaDoc[] classpath) {
87         AbstractBundleData abstractbundledata = (AbstractBundleData) bundledata;
88         try {
89             bundledata.open(); /* make sure the BundleData is open */
90         } catch (IOException e) {
91
92             abstractbundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, abstractbundledata.getBundle(), e);
93         }
94         ClasspathEntry[] fragEntries = buildClasspath(classpath, abstractbundledata, domain);
95         FragmentClasspath fragClasspath = new FragmentClasspath(fragEntries, abstractbundledata, domain);
96         insertFragment(fragClasspath);
97     }
98
99     /**
100      * Inserts a fragment classpath to into the list of fragments for this host.
101      * Fragments are inserted into the list according to the fragment's
102      * Bundle ID.
103      * @param fragClasspath The FragmentClasspath to insert.
104      */

105     protected synchronized void insertFragment(FragmentClasspath fragClasspath) {
106         if (fragClasspaths == null) {
107             // First fragment to attach. Simply create the list and add the fragment.
108
fragClasspaths = new Vector(10);
109             fragClasspaths.addElement(fragClasspath);
110             return;
111         }
112
113         // Find a place in the fragment list to insert this fragment.
114
int size = fragClasspaths.size();
115         long fragID = fragClasspath.bundledata.getBundleID();
116         for (int i = 0; i < size; i++) {
117             long otherID = ((FragmentClasspath) fragClasspaths.elementAt(i)).bundledata.getBundleID();
118             if (fragID < otherID) {
119                 fragClasspaths.insertElementAt(fragClasspath, i);
120                 return;
121             }
122         }
123         // This fragment has the highest ID; put it at the end of the list.
124
fragClasspaths.addElement(fragClasspath);
125     }
126
127     /**
128      * Gets a ClasspathEntry object for the specified ClassPath entry.
129      * @param cp The ClassPath entry to get the ClasspathEntry for.
130      * @param bundledata The BundleData that the ClassPath entry is for.
131      * @param domain The ProtectionDomain for the ClassPath entry.
132      * @return The ClasspathEntry object for the ClassPath entry.
133      */

134     protected ClasspathEntry getClasspath(String JavaDoc cp, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
135         BundleFile bundlefile = null;
136         File file = bundledata.getBaseBundleFile().getFile(cp);
137         if (file != null && file.exists()) {
138             try {
139                 bundlefile = hostdata.getAdaptor().createBundleFile(file, bundledata);
140             } catch (IOException e) {
141                 bundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundledata.getBundle(), e);
142             }
143         } else if (bundledata.getBaseBundleFile().containsDir(cp)) {
144             // the classpath entry is a directory in the bundle jar file.
145
bundlefile = new BundleFile.NestedDirBundleFile(bundledata.getBaseBundleFile(), cp);
146         }
147
148         // if in dev mode, try using the cp as an absolute path
149
if (bundlefile == null && DevClassPathHelper.inDevelopmentMode()) {
150             file = new File(cp);
151             if (file.exists() && file.isAbsolute())
152                 // if the file exists and is absolute then create BundleFile for it.
153
try {
154                     bundlefile = hostdata.getAdaptor().createBundleFile(file, bundledata);
155                 } catch (IOException e) {
156                     bundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundledata.getBundle(), e);
157                 }
158         }
159
160         if (bundlefile != null)
161             return createClassPathEntry(bundlefile, domain);
162         else
163             return null;
164     }
165
166     protected synchronized Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
167         Class JavaDoc result = findLoadedClass(name);
168         if (result != null)
169             return result;
170         for (int i = 0; i < classpathEntries.length; i++) {
171             if (classpathEntries[i] != null) {
172                 result = findClassImpl(name, classpathEntries[i]);
173                 if (result != null) {
174                     return result;
175                 }
176             }
177         }
178         // look in fragments.
179
if (fragClasspaths != null) {
180             int size = fragClasspaths.size();
181             for (int i = 0; i < size; i++) {
182                 FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
183                 for (int j = 0; j < fragCP.classpathEntries.length; j++) {
184                     result = findClassImpl(name, fragCP.classpathEntries[j]);
185                     if (result != null) {
186                         return result;
187                     }
188                 }
189             }
190         }
191         throw new ClassNotFoundException JavaDoc(name);
192     }
193
194     /**
195      * Finds a class in the BundleFile. If a class is found then the class
196      * is defined using the ProtectionDomain bundledomain.
197      * @param name The name of the class to find.
198      * @param classpathEntry The ClasspathEntry to find the class in.
199      * @return The loaded class object or null if the class is not found.
200      */

201     protected Class JavaDoc findClassImpl(String JavaDoc name, ClasspathEntry classpathEntry) {
202         if (Debug.DEBUG && Debug.DEBUG_LOADER) {
203             Debug.println("BundleClassLoader[" + hostdata + "].findClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
204
}
205
206         String JavaDoc filename = name.replace('.', '/').concat(".class"); //$NON-NLS-1$
207

208         BundleEntry entry = classpathEntry.getBundleFile().getEntry(filename);
209
210         if (entry == null) {
211             return null;
212         }
213
214         InputStream in;
215         try {
216             in = entry.getInputStream();
217         } catch (IOException e) {
218             return null;
219         }
220
221         int length = (int) entry.getSize();
222         byte[] classbytes;
223         int bytesread = 0;
224         int readcount;
225
226         if (Debug.DEBUG && Debug.DEBUG_LOADER) {
227             Debug.println(" about to read " + length + " bytes from " + filename); //$NON-NLS-1$ //$NON-NLS-2$
228
}
229
230         try {
231             try {
232                 if (length > 0) {
233                     classbytes = new byte[length];
234
235                     readloop: for (; bytesread < length; bytesread += readcount) {
236                         readcount = in.read(classbytes, bytesread, length - bytesread);
237
238                         if (readcount <= 0) /* if we didn't read anything */{
239                             break readloop; /* leave the loop */
240                         }
241                     }
242                 } else /* BundleEntry does not know its own length! */{
243                     length = buffersize;
244                     classbytes = new byte[length];
245
246                     readloop: while (true) {
247                         for (; bytesread < length; bytesread += readcount) {
248                             readcount = in.read(classbytes, bytesread, length - bytesread);
249
250                             if (readcount <= 0) /* if we didn't read anything */{
251                                 break readloop; /* leave the loop */
252                             }
253                         }
254
255                         byte[] oldbytes = classbytes;
256                         length += buffersize;
257                         classbytes = new byte[length];
258                         System.arraycopy(oldbytes, 0, classbytes, 0, bytesread);
259                     }
260                 }
261             } catch (IOException e) {
262                 if (Debug.DEBUG && Debug.DEBUG_LOADER) {
263                     Debug.println(" IOException reading " + filename + " from " + hostdata); //$NON-NLS-1$ //$NON-NLS-2$
264
}
265
266                 return null;
267             }
268         } finally {
269             try {
270                 in.close();
271             } catch (IOException ee) {
272             }
273         }
274
275         if (Debug.DEBUG && Debug.DEBUG_LOADER) {
276             Debug.println(" read " + bytesread + " bytes from " + filename); //$NON-NLS-1$ //$NON-NLS-2$
277
Debug.println(" defining class " + name); //$NON-NLS-1$
278
}
279
280         try {
281             return (defineClass(name, classbytes, 0, bytesread, classpathEntry));
282         } catch (Error JavaDoc e) {
283             if (Debug.DEBUG && Debug.DEBUG_LOADER) {
284                 Debug.println(" error defining class " + name); //$NON-NLS-1$
285
}
286
287             throw e;
288         }
289     }
290
291     protected Class JavaDoc defineClass(String JavaDoc name, byte[] classbytes, int off, int len, ClasspathEntry classpathEntry) throws ClassFormatError JavaDoc {
292         if (name != null && name.startsWith("java.")) { //$NON-NLS-1$
293
// To work around the security issue that prevents any
294
// other classloader except for the bootstrap classloader
295
// from loading packages that start with java.
296
name = null;
297         }
298         return defineClass(name, classbytes, off, len, classpathEntry.getProtectionDomain());
299     }
300
301     protected URL JavaDoc findResource(String JavaDoc name) {
302         URL JavaDoc result = null;
303         for (int i = 0; i < classpathEntries.length; i++) {
304             if (classpathEntries[i] != null) {
305                 result = findResourceImpl(name, classpathEntries[i].getBundleFile());
306                 if (result != null) {
307                     return result;
308                 }
309             }
310         }
311         // look in fragments
312
if (fragClasspaths != null) {
313             int size = fragClasspaths.size();
314             for (int i = 0; i < size; i++) {
315                 FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
316                 for (int j = 0; j < fragCP.classpathEntries.length; j++) {
317                     result = findResourceImpl(name, fragCP.classpathEntries[j].getBundleFile());
318                     if (result != null) {
319                         return result;
320                     }
321                 }
322             }
323         }
324         return null;
325     }
326
327     /**
328      * Looks in the specified BundleFile for the resource.
329      * @param name The name of the resource to find,.
330      * @param bundlefile The BundleFile to look in.
331      * @return A URL to the resource or null if the resource does not exist.
332      */

333     protected URL JavaDoc findResourceImpl(String JavaDoc name, BundleFile bundlefile) {
334         return bundlefile.getResourceURL(name, hostdata.getBundleID());
335     }
336
337     /**
338      * @see org.eclipse.osgi.framework.adaptor.BundleClassLoader#findLocalResources(String)
339      */

340     public Enumeration findLocalResources(String JavaDoc resource) {
341         Vector resources = new Vector(6);
342         for (int i = 0; i < classpathEntries.length; i++) {
343             if (classpathEntries[i] != null) {
344                 URL JavaDoc url = findResourceImpl(resource, classpathEntries[i].getBundleFile());
345                 if (url != null) {
346                     resources.addElement(url);
347                 }
348             }
349         }
350         // look in fragments
351
if (fragClasspaths != null) {
352             int size = fragClasspaths.size();
353             for (int i = 0; i < size; i++) {
354                 FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
355                 for (int j = 0; j < fragCP.classpathEntries.length; j++) {
356                     URL JavaDoc url = findResourceImpl(resource, fragCP.classpathEntries[j].getBundleFile());
357                     if (url != null) {
358                         resources.addElement(url);
359                     }
360                 }
361             }
362         }
363         if (resources.size() > 0) {
364             return resources.elements();
365         }
366         return null;
367     }
368
369     public Object JavaDoc findLocalObject(String JavaDoc object) {
370         BundleEntry result = null;
371         for (int i = 0; i < classpathEntries.length; i++) {
372             if (classpathEntries[i] != null) {
373                 result = findObjectImpl(object, classpathEntries[i].getBundleFile());
374                 if (result != null) {
375                     return result;
376                 }
377             }
378         }
379         // look in fragments
380
if (fragClasspaths != null) {
381             int size = fragClasspaths.size();
382             for (int i = 0; i < size; i++) {
383                 FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
384                 for (int j = 0; j < fragCP.classpathEntries.length; j++) {
385                     result = findObjectImpl(object, fragCP.classpathEntries[j].getBundleFile());
386                     if (result != null) {
387                         return result;
388                     }
389                 }
390             }
391         }
392         return null;
393     }
394
395     protected BundleEntry findObjectImpl(String JavaDoc object, BundleFile bundleFile) {
396         return bundleFile.getEntry(object);
397     }
398
399     /**
400      * Closes all the BundleFile objects for this BundleClassLoader.
401      */

402     public void close() {
403         // do not close if we are shutting down
404
if (closed || hostdata.getAdaptor().isStopping())
405             return;
406
407         super.close();
408         if (classpathEntries != null) {
409             for (int i = 0; i < classpathEntries.length; i++) {
410                 if (classpathEntries[i] != null) {
411                     try {
412                         if (classpathEntries[i].getBundleFile() != hostdata.getBaseBundleFile()) {
413                             classpathEntries[i].getBundleFile().close();
414                         }
415                     } catch (IOException e) {
416                         hostdata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, hostdata.getBundle(), e);
417                     }
418                 }
419             }
420         }
421         if (fragClasspaths != null) {
422             int size = fragClasspaths.size();
423             for (int i = 0; i < size; i++) {
424                 FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
425                 fragCP.close();
426             }
427         }
428     }
429
430     protected ClasspathEntry[] buildClasspath(String JavaDoc[] classpath, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
431         ArrayList result = new ArrayList(10);
432
433         // If not in dev mode then just add the regular classpath entries and return
434
if (!DevClassPathHelper.inDevelopmentMode()) {
435             for (int i = 0; i < classpath.length; i++)
436                 findClassPathEntry(result, classpath[i], bundledata, domain);
437             return (ClasspathEntry[]) result.toArray(new ClasspathEntry[result.size()]);
438         }
439
440         // Otherwise, add the legacy entries for backwards compatibility and
441
// then for each classpath entry add the dev entries as spec'd in the
442
// corresponding properties file. If none are spec'd, add the
443
// classpath entry itself
444
addDefaultDevEntries(result, bundledata, domain);
445         for (int i = 0; i < classpath.length; i++) {
446             String JavaDoc[] devEntries = getDevEntries(classpath[i], bundledata);
447             if (devEntries != null && devEntries.length > 0) {
448                 for (int j = 0; j < devEntries.length; j++)
449                     findClassPathEntry(result, devEntries[j], bundledata, domain);
450             } else
451                 findClassPathEntry(result, classpath[i], bundledata, domain);
452         }
453         return (ClasspathEntry[]) result.toArray(new ClasspathEntry[result.size()]);
454     }
455
456     protected void addDefaultDevEntries(ArrayList result, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
457         String JavaDoc[] devClassPath = DevClassPathHelper.getDevClassPath(bundledata.getSymbolicName());
458         if (devClassPath == null)
459             return;
460         for (int i = 0; i < devClassPath.length; i++)
461             findClassPathEntry(result, devClassPath[i], bundledata, domain);
462     }
463
464     protected void findClassPathEntry(ArrayList result, String JavaDoc entry, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
465         if (!addClassPathEntry(result, entry, bundledata, domain)) {
466             // if (devCP == null) {
467
// BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_CLASSPATH_ENTRY_NOT_FOUND_EXCEPTION", entry, hostdata.getLocation()));
468
// bundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundledata.getBundle(), be);
469
// }
470
}
471     }
472
473     protected boolean addClassPathEntry(ArrayList result, String JavaDoc entry, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
474         if (entry.equals(".")) { //$NON-NLS-1$
475
result.add(createClassPathEntry(bundledata.getBaseBundleFile(), domain));
476             return true;
477         }
478         Object JavaDoc element = getClasspath(entry, bundledata, domain);
479         if (element != null) {
480             result.add(element);
481             return true;
482         }
483         // need to check in fragments for the classpath entry.
484
// only check for fragments if the bundledata is the hostdata.
485
if (fragClasspaths != null && hostdata == bundledata) {
486             int size = fragClasspaths.size();
487             for (int i = 0; i < size; i++) {
488                 FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
489                 element = getClasspath(entry, fragCP.bundledata, fragCP.domain);
490                 if (element != null) {
491                     result.add(element);
492                     return true;
493                 }
494             }
495         }
496         return false;
497     }
498
499     protected String JavaDoc[] getDevEntries(String JavaDoc classpathEntry, AbstractBundleData bundledata) {
500         File propLocation = bundledata.getBaseBundleFile().getFile(classpathEntry + ".properties"); //$NON-NLS-1$
501
if (propLocation == null)
502             return null;
503         try {
504             InputStream in = new FileInputStream(propLocation);
505             try {
506                 Properties devProps = new Properties();
507                 devProps.load(in);
508                 return DevClassPathHelper.getArrayFromList(devProps.getProperty("bin")); //$NON-NLS-1$
509
} finally {
510                 in.close();
511             }
512         } catch (IOException e) {
513             BundleException be = new BundleException(AdaptorMsg.formatter.getString("BUNDLE_CLASSPATH_PROPERTIES_ERROR", propLocation), e); //$NON-NLS-1$
514
bundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundledata.getBundle(), be);
515         }
516         return null;
517     }
518
519     /**
520      * Creates a ClasspathEntry from a BundleFile and ProtectionDomain.
521      * @param bundlefile the BundleFile.
522      * @param domain the ProtectionDomain
523      * @return the ClasspathEntry
524      */

525     protected ClasspathEntry createClassPathEntry(BundleFile bundlefile, ProtectionDomain JavaDoc domain) {
526         return new ClasspathEntry(bundlefile, domain);
527     }
528
529     /**
530      * A data structure to hold information about a fragment classpath.
531      */

532     protected class FragmentClasspath {
533         /** The ClasspathEntries of the fragments Bundle-Classpath */
534         protected ClasspathEntry[] classpathEntries;
535         /** The BundleData of the fragment */
536         protected AbstractBundleData bundledata;
537         /** The ProtectionDomain of the fragment */
538         protected ProtectionDomain JavaDoc domain;
539
540         protected FragmentClasspath(ClasspathEntry[] classpathEntries, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
541             this.classpathEntries = classpathEntries;
542             this.bundledata = bundledata;
543             this.domain = domain;
544         }
545
546         protected void close() {
547             for (int i = 0; i < classpathEntries.length; i++) {
548                 try {
549                     if (classpathEntries[i].getBundleFile() != bundledata.getBaseBundleFile()) {
550                         classpathEntries[i].getBundleFile().close();
551                     }
552                 } catch (IOException e) {
553                     bundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundledata.getBundle(), e);
554                 }
555             }
556         }
557     }
558
559     /**
560      * A data structure to hold information about a classpath entry.
561      */

562     protected class ClasspathEntry {
563         protected BundleFile bundlefile;
564         protected ProtectionDomain JavaDoc domain;
565
566         protected ClasspathEntry(BundleFile bundlefile, ProtectionDomain JavaDoc domain) {
567             this.bundlefile = bundlefile;
568             this.domain = domain;
569         }
570
571         public BundleFile getBundleFile() {
572             return bundlefile;
573         }
574
575         public ProtectionDomain JavaDoc getProtectionDomain() {
576             return domain;
577         }
578     }
579 }
Popular Tags