KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > runtime > adaptor > EclipseClassLoader


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

11 package org.eclipse.core.runtime.adaptor;
12
13 import java.io.IOException JavaDoc;
14 import java.io.InputStream JavaDoc;
15 import java.net.URL JavaDoc;
16 import java.security.ProtectionDomain JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.StringTokenizer JavaDoc;
19 import java.util.jar.Attributes JavaDoc;
20 import java.util.jar.Manifest JavaDoc;
21 import org.eclipse.core.runtime.internal.adaptor.BundleStopper;
22 import org.eclipse.core.runtime.internal.adaptor.EclipseEnvironmentInfo;
23 import org.eclipse.core.runtime.internal.stats.*;
24 import org.eclipse.osgi.framework.adaptor.*;
25 import org.eclipse.osgi.framework.adaptor.core.*;
26 import org.eclipse.osgi.framework.internal.core.AbstractBundle;
27 import org.eclipse.osgi.framework.internal.core.Msg;
28 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
29 import org.eclipse.osgi.util.NLS;
30 import org.osgi.framework.*;
31
32 /**
33  * Implements the class loader for Eclipse
34  * <p>
35  * Clients may extend this class.
36  * </p>
37  * @since 3.1
38  */

39 public class EclipseClassLoader extends DefaultClassLoader {
40     private static String JavaDoc[] NL_JAR_VARIANTS = buildNLJarVariants(EclipseEnvironmentInfo.getDefault().getNL());
41     private static boolean DEFINE_PACKAGES;
42     private static final String JavaDoc VARIABLE_DELIM_STRING = "$"; //$NON-NLS-1$
43
private static final char VARIABLE_DELIM_CHAR = '$';
44     private static final String JavaDoc EXTERNAL_LIB_PREFIX = "external:"; //$NON-NLS-1$
45
static {
46         try {
47             Class.forName("java.lang.Package"); //$NON-NLS-1$
48
DEFINE_PACKAGES = true;
49         } catch (ClassNotFoundException JavaDoc e) {
50             DEFINE_PACKAGES = false;
51         }
52     }
53
54     public EclipseClassLoader(ClassLoaderDelegate delegate, ProtectionDomain JavaDoc domain, String JavaDoc[] classpath, ClassLoader JavaDoc parent, BundleData bundleData) {
55         super(delegate, domain, classpath, parent, (EclipseBundleData) bundleData);
56     }
57
58     public Class JavaDoc findLocalClass(String JavaDoc className) throws ClassNotFoundException JavaDoc {
59         if (StatsManager.MONITOR_CLASSES) //Suport for performance analysis
60
ClassloaderStats.startLoadingClass(getClassloaderId(), className);
61         boolean found = true;
62
63         try {
64             AbstractBundle bundle = (AbstractBundle) hostdata.getBundle();
65             // If the bundle is active, uninstalled or stopping then the bundle has already
66
// been initialized (though it may have been destroyed) so just return the class.
67
if ((bundle.getState() & (Bundle.ACTIVE | Bundle.UNINSTALLED | Bundle.STOPPING)) != 0)
68                 return basicFindLocalClass(className);
69
70             // The bundle is not active and does not require activation, just return the class
71
if (!shouldActivateFor(className))
72                 return basicFindLocalClass(className);
73
74             // The bundle is starting. Note that if the state changed between the tests
75
// above and this test (e.g., it was not ACTIVE but now is), that's ok, we will
76
// just try to start it again (else case).
77
// TODO need an explanation here of why we duplicated the mechanism
78
// from the framework rather than just calling start() and letting it sort it out.
79
if (bundle.getState() == Bundle.STARTING) {
80                 // If the thread trying to load the class is the one trying to activate the bundle, then return the class
81
if (bundle.testStateChanging(Thread.currentThread()) || bundle.testStateChanging(null))
82                     return basicFindLocalClass(className);
83
84                 // If it's another thread, we wait and try again. In any case the class is returned.
85
// The difference is that an exception can be logged.
86
// TODO do we really need this test? We just did it on the previous line?
87
if (!bundle.testStateChanging(Thread.currentThread())) {
88                     Thread JavaDoc threadChangingState = bundle.getStateChanging();
89                     if (StatsManager.TRACE_BUNDLES && threadChangingState != null) {
90                         System.out.println("Concurrent startup of bundle " + bundle.getSymbolicName() + " by " + Thread.currentThread() + " and " + threadChangingState.getName() + ". Waiting up to 5000ms for " + threadChangingState + " to finish the initialization."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
91
}
92                     long start = System.currentTimeMillis();
93                     long delay = 5000;
94                     long timeLeft = delay;
95                     while (true) {
96                         try {
97                             Thread.sleep(100); // do not release the classloader lock (bug 86713)
98
if (bundle.testStateChanging(null) || timeLeft <= 0)
99                                 break;
100                         } catch (InterruptedException JavaDoc e) {
101                             //Ignore and keep waiting
102
}
103                         timeLeft = start + delay - System.currentTimeMillis();
104                     }
105                     if (timeLeft <= 0 || bundle.getState() != Bundle.ACTIVE) {
106                         String JavaDoc bundleName = bundle.getSymbolicName() == null ? Long.toString(bundle.getBundleId()) : bundle.getSymbolicName();
107                         String JavaDoc message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_CONCURRENT_STARTUP, new Object JavaDoc[] {Thread.currentThread().getName(), className, threadChangingState.getName(), bundleName, Long.toString(delay)});
108                         EclipseAdaptor.getDefault().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, message, 0, new Exception JavaDoc(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_GENERATED_EXCEPTION), null));
109                     }
110                     return basicFindLocalClass(className);
111                 }
112             }
113
114             //The bundle must be started.
115
try {
116                 hostdata.getBundle().start();
117             } catch (BundleException e) {
118                 String JavaDoc message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_ACTIVATION, bundle.getSymbolicName(), Long.toString(bundle.getBundleId())); //$NON-NLS-1$
119
EclipseAdaptor.getDefault().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, message, 0, e, null));
120                 throw new ClassNotFoundException JavaDoc(className, e);
121             }
122             return basicFindLocalClass(className);
123         } catch (ClassNotFoundException JavaDoc e) {
124             found = false;
125             throw e;
126         } finally {
127             if (StatsManager.MONITOR_CLASSES)
128                 ClassloaderStats.endLoadingClass(getClassloaderId(), className, found);
129         }
130     }
131
132     /**
133      * Do the basic work for finding a class. This avoids the activation detection etc
134      * and can be used by subclasses to override the default (from the superclass)
135      * way of finding classes.
136      * @param name the class to look for
137      * @return the found class
138      * @throws ClassNotFoundException if the requested class cannot be found
139      */

140     protected Class JavaDoc basicFindLocalClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
141         return super.findLocalClass(name);
142     }
143
144     /**
145      * Determines if for loading the given class we should activate the bundle.
146      */

147     private boolean shouldActivateFor(String JavaDoc className) throws ClassNotFoundException JavaDoc {
148         //Don't reactivate on shut down
149
if (hostdata.getAdaptor().isStopping()) {
150             BundleStopper stopper = EclipseAdaptor.getDefault().getBundleStopper();
151             if (stopper != null && stopper.isStopped(hostdata.getBundle())) {
152                 String JavaDoc message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_ALREADY_STOPPED, className, hostdata.getSymbolicName());
153                 throw new ClassNotFoundException JavaDoc(message);
154             }
155         }
156         boolean autoStart = ((EclipseBundleData) hostdata).isAutoStart();
157         String JavaDoc[] autoStartExceptions = ((EclipseBundleData) hostdata).getAutoStartExceptions();
158         // no exceptions, it is easy to figure it out
159
if (autoStartExceptions == null)
160             return autoStart;
161         // otherwise, we need to check if the package is in the exceptions list
162
int dotPosition = className.lastIndexOf('.');
163         // the class has no package name... no exceptions apply
164
if (dotPosition == -1)
165             return autoStart;
166         String JavaDoc packageName = className.substring(0, dotPosition);
167         // should activate if autoStart and package is not an exception, or if !autoStart and package is exception
168
return autoStart ^ contains(autoStartExceptions, packageName);
169     }
170
171     private boolean contains(String JavaDoc[] array, String JavaDoc element) {
172         for (int i = 0; i < array.length; i++)
173             if (array[i].equals(element))
174                 return true;
175         return false;
176     }
177
178     /**
179      * Override defineClass to allow for package defining.
180      */

181     protected Class JavaDoc defineClass(String JavaDoc name, byte[] classbytes, int off, int len, ClasspathEntry classpathEntry) throws ClassFormatError JavaDoc {
182         if (!DEFINE_PACKAGES)
183             return super.defineClass(name, classbytes, off, len, classpathEntry);
184
185         // Define the package if it is not the default package.
186
int lastIndex = name.lastIndexOf('.');
187         if (lastIndex != -1) {
188             String JavaDoc packageName = name.substring(0, lastIndex);
189             Package JavaDoc pkg = getPackage(packageName);
190             if (pkg == null) {
191                 // get info about the package from the classpath entry's manifest.
192
String JavaDoc specTitle = null, specVersion = null, specVendor = null, implTitle = null, implVersion = null, implVendor = null;
193                 Manifest JavaDoc mf = ((EclipseClasspathEntry) classpathEntry).getManifest();
194                 if (mf != null) {
195                     Attributes JavaDoc mainAttributes = mf.getMainAttributes();
196                     String JavaDoc dirName = packageName.replace('.', '/') + '/';
197                     Attributes JavaDoc packageAttributes = mf.getAttributes(dirName);
198                     boolean noEntry = false;
199                     if (packageAttributes == null) {
200                         noEntry = true;
201                         packageAttributes = mainAttributes;
202                     }
203                     specTitle = packageAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE);
204                     if (specTitle == null && !noEntry)
205                         specTitle = mainAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE);
206                     specVersion = packageAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
207                     if (specVersion == null && !noEntry)
208                         specVersion = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
209                     specVendor = packageAttributes.getValue(Attributes.Name.SPECIFICATION_VENDOR);
210                     if (specVendor == null && !noEntry)
211                         specVendor = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VENDOR);
212                     implTitle = packageAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
213                     if (implTitle == null && !noEntry)
214                         implTitle = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
215                     implVersion = packageAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
216                     if (implVersion == null && !noEntry)
217                         implVersion = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
218                     implVendor = packageAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
219                     if (implVendor == null && !noEntry)
220                         implVendor = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
221                 }
222                 // The package is not defined yet define it before we define the class.
223
// TODO still need to seal packages.
224
definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, null);
225             }
226         }
227         return super.defineClass(name, classbytes, off, len, classpathEntry);
228     }
229
230     private String JavaDoc getClassloaderId() {
231         return hostdata.getBundle().getSymbolicName();
232     }
233
234     public URL JavaDoc getResource(String JavaDoc name) {
235         URL JavaDoc result = super.getResource(name);
236         if (StatsManager.MONITOR_RESOURCES) {
237             if (result != null && name.endsWith(".properties")) { //$NON-NLS-1$
238
ClassloaderStats.loadedBundle(getClassloaderId(), new ResourceBundleStats(getClassloaderId(), name, result));
239             }
240         }
241         return result;
242     }
243
244     protected void findClassPathEntry(ArrayList JavaDoc result, String JavaDoc entry, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
245         String JavaDoc var = hasPrefix(entry);
246         if (var != null) {
247             // find internal library using eclipse predefined vars
248
findInternalClassPath(var, result, entry, bundledata, domain);
249             return;
250         }
251         if (entry.startsWith(EXTERNAL_LIB_PREFIX)) {
252             entry = entry.substring(EXTERNAL_LIB_PREFIX.length());
253             // find external library using system property substitution
254
ClasspathEntry cpEntry = getExternalClassPath(substituteVars(entry), bundledata, domain);
255             if (cpEntry != null)
256                 result.add(cpEntry);
257             return;
258         }
259         // if we get here just do the default searching
260
super.findClassPathEntry(result, entry, bundledata, domain);
261     }
262
263     private void findInternalClassPath(String JavaDoc var, ArrayList JavaDoc result, String JavaDoc entry, AbstractBundleData bundledata, ProtectionDomain JavaDoc domain) {
264         if (var.equals("ws")) { //$NON-NLS-1$
265
super.findClassPathEntry(result, "ws/" + EclipseEnvironmentInfo.getDefault().getWS() + entry.substring(4), bundledata, domain); //$NON-NLS-1$
266
return;
267         }
268         if (var.equals("os")) { //$NON-NLS-1$
269
super.findClassPathEntry(result, "os/" + EclipseEnvironmentInfo.getDefault().getOS() + entry.substring(4), bundledata, domain); //$NON-NLS-1$
270
return;
271         }
272         if (var.equals("nl")) { //$NON-NLS-1$
273
entry = entry.substring(4);
274             for (int i = 0; i < NL_JAR_VARIANTS.length; i++) {
275                 if (addClassPathEntry(result, "nl/" + NL_JAR_VARIANTS[i] + entry, bundledata, domain)) //$NON-NLS-1$ //$NON-NLS-2$
276
return;
277             }
278             // is we are not in development mode, post some framework errors.
279
if (!DevClassPathHelper.inDevelopmentMode()) {
280                 //BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_CLASSPATH_ENTRY_NOT_FOUND_EXCEPTION", entry, hostdata.getLocation())); //$NON-NLS-1$
281
BundleException be = new BundleException(NLS.bind(Msg.BUNDLE_CLASSPATH_ENTRY_NOT_FOUND_EXCEPTION, entry)); //$NON-NLS-1$
282
bundledata.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundledata.getBundle(), be);
283             }
284         }
285     }
286
287     private static String JavaDoc[] buildNLJarVariants(String JavaDoc nl) {
288         ArrayList JavaDoc result = new ArrayList JavaDoc();
289         nl = nl.replace('_', '/');
290         while (nl.length() > 0) {
291             result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$
292
int i = nl.lastIndexOf('/'); //$NON-NLS-1$
293
nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$
294
}
295         result.add(""); //$NON-NLS-1$
296
return (String JavaDoc[]) result.toArray(new String JavaDoc[result.size()]);
297     }
298
299     //return a String representing the string found between the $s
300
private String JavaDoc hasPrefix(String JavaDoc libPath) {
301         if (libPath.startsWith("$ws$")) //$NON-NLS-1$
302
return "ws"; //$NON-NLS-1$
303
if (libPath.startsWith("$os$")) //$NON-NLS-1$
304
return "os"; //$NON-NLS-1$
305
if (libPath.startsWith("$nl$")) //$NON-NLS-1$
306
return "nl"; //$NON-NLS-1$
307
return null;
308     }
309
310     private String JavaDoc substituteVars(String JavaDoc cp) {
311         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(cp.length());
312         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(cp, VARIABLE_DELIM_STRING, true);
313         boolean varStarted = false; // indicates we are processing a var subtitute
314
String JavaDoc var = null; // the current var key
315
while (st.hasMoreElements()) {
316             String JavaDoc tok = st.nextToken();
317             if (VARIABLE_DELIM_STRING.equals(tok)) {
318                 if (!varStarted) {
319                     varStarted = true; // we found the start of a var
320
var = ""; //$NON-NLS-1$
321
} else {
322                     // we have found the end of a var
323
String JavaDoc prop = null;
324                     // get the value of the var from system properties
325
if (var != null && var.length() > 0)
326                         prop = System.getProperty(var);
327                     if (prop != null)
328                         // found a value; use it
329
buf.append(prop);
330                     else
331                         // could not find a value append the var name w/o delims
332
buf.append(var == null ? "" : var); //$NON-NLS-1$
333
varStarted = false;
334                     var = null;
335                 }
336             } else {
337                 if (!varStarted)
338                     buf.append(tok); // the token is not part of a var
339
else
340                     var = tok; // the token is the var key; save the key to process when we find the end token
341
}
342         }
343         if (var != null)
344             // found a case of $var at the end of the cp with no trailing $; just append it as is.
345
buf.append(VARIABLE_DELIM_CHAR).append(var);
346         return buf.toString();
347     }
348
349     /**
350      * Override to create EclipseClasspathEntry objects. EclipseClasspathEntry
351      * allows access to the manifest file for the classpath entry.
352      */

353     protected ClasspathEntry createClassPathEntry(BundleFile bundlefile, ProtectionDomain JavaDoc domain) {
354         return new EclipseClasspathEntry(bundlefile, domain);
355     }
356
357     /**
358      * A ClasspathEntry that has a manifest associated with it.
359      */

360     protected class EclipseClasspathEntry extends ClasspathEntry {
361         Manifest JavaDoc mf;
362         boolean initMF = false;
363
364         protected EclipseClasspathEntry(BundleFile bundlefile, ProtectionDomain JavaDoc domain) {
365             super(bundlefile, domain);
366         }
367
368         public Manifest JavaDoc getManifest() {
369             if (initMF)
370                 return mf;
371             if (!hasPackageInfo()) {
372                 initMF = true;
373                 mf = null;
374                 return mf;
375             }
376             BundleEntry mfEntry = getBundleFile().getEntry(org.eclipse.osgi.framework.internal.core.Constants.OSGI_BUNDLE_MANIFEST);
377             if (mfEntry != null)
378                 try {
379                     InputStream JavaDoc manIn = mfEntry.getInputStream();
380                     mf = new Manifest JavaDoc(manIn);
381                     manIn.close();
382                 } catch (IOException JavaDoc e) {
383                     // do nothing
384
}
385             initMF = true;
386             return mf;
387         }
388
389         private boolean hasPackageInfo() {
390             if (getBundleFile() == getHostData().getBaseBundleFile())
391                 return ((EclipseBundleData)getHostData()).hasPackageInfo;
392             FragmentClasspath[] fragCPs = getFragClasspaths();
393             if (fragCPs != null)
394                 for (int i = 0; i < fragCPs.length; i++)
395                     if (getBundleFile() == fragCPs[i].getBundleData().getBaseBundleFile())
396                         return ((EclipseBundleData)fragCPs[i].getBundleData()).hasPackageInfo;
397             return true;
398         }
399     }
400 }
401
Popular Tags