blob: a1a5190a3be728ae914b8a51b5dbf2fc8a982bfc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.runtime.internal.adaptor;
import java.net.URL;
import org.eclipse.core.runtime.internal.stats.StatsManager;
import org.eclipse.osgi.baseadaptor.*;
import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingStatsHook;
import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
import org.eclipse.osgi.framework.internal.core.AbstractBundle;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
public class EclipseLazyStarter implements ClassLoadingStatsHook, HookConfigurator {
public void preFindLocalClass(String name, ClasspathManager manager) throws ClassNotFoundException {
AbstractBundle bundle = (AbstractBundle) manager.getBaseData().getBundle();
// If the bundle is active, uninstalled or stopping then the bundle has already
// been initialized (though it may have been destroyed) so just return the class.
if ((bundle.getState() & (Bundle.ACTIVE | Bundle.UNINSTALLED | Bundle.STOPPING)) != 0)
return;
// The bundle is not active and does not require activation, just return the class
if (!shouldActivateFor(name, manager.getBaseData()))
return;
// The bundle is starting. Note that if the state changed between the tests
// above and this test (e.g., it was not ACTIVE but now is), that's ok, we will
// just try to start it again (else case).
// TODO need an explanation here of why we duplicated the mechanism
// from the framework rather than just calling start() and letting it sort it out.
if (bundle.getState() == Bundle.STARTING) {
// If the thread trying to load the class is the one trying to activate the bundle, then return the class
if (bundle.testStateChanging(Thread.currentThread()) || bundle.testStateChanging(null))
return;
// If it's another thread, we wait and try again. In any case the class is returned.
// The difference is that an exception can be logged.
// TODO do we really need this test? We just did it on the previous line?
if (!bundle.testStateChanging(Thread.currentThread())) {
Thread threadChangingState = bundle.getStateChanging();
if (StatsManager.TRACE_BUNDLES && threadChangingState != null)
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$
long start = System.currentTimeMillis();
long delay = 5000;
long timeLeft = delay;
while (true) {
try {
Thread.sleep(100); // do not release the classloader lock (bug 86713)
if (bundle.testStateChanging(null) || timeLeft <= 0)
break;
} catch (InterruptedException e) {
//Ignore and keep waiting
}
timeLeft = start + delay - System.currentTimeMillis();
}
if (timeLeft <= 0 || bundle.getState() != Bundle.ACTIVE) {
String bundleName = bundle.getSymbolicName() == null ? Long.toString(bundle.getBundleId()) : bundle.getSymbolicName();
String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_CONCURRENT_STARTUP, new Object[] {Thread.currentThread().getName(), name, threadChangingState.getName(), bundleName, Long.toString(delay)});
manager.getBaseData().getAdaptor().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, new Exception(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_GENERATED_EXCEPTION), null));
}
return;
}
}
//The bundle must be started.
try {
bundle.start();
} catch (BundleException e) {
String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_ACTIVATION, bundle.getSymbolicName(), Long.toString(bundle.getBundleId()));
manager.getBaseData().getAdaptor().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null));
throw new ClassNotFoundException(name, e);
}
return;
}
public void postFindLocalClass(String name, Class clazz, ClasspathManager manager) {
// do nothing
}
public void preFindLocalResource(String name, ClasspathManager manager) {
// do nothing
}
public void postFindLocalResource(String name, URL resource, ClasspathManager manager) {
// do nothing
}
public void recordClassDefine(String name, Class clazz, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
// do nothing
}
private boolean shouldActivateFor(String className, BaseData bundledata) throws ClassNotFoundException {
if (!isAutoStartable(className, bundledata))
return false;
//Don't reactivate on shut down
if (bundledata.getAdaptor().isStopping()) {
BundleStopper stopper = getBundleStopper(bundledata);
if (stopper != null && stopper.isStopped(bundledata.getBundle())) {
String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_ALREADY_STOPPED, className, bundledata.getSymbolicName());
throw new ClassNotFoundException(message);
}
}
return true;
}
private boolean isAutoStartable(String className, BaseData bundledata) {
EclipseStorageHook storageHook = (EclipseStorageHook) bundledata.getStorageHook(EclipseStorageHook.KEY);
if (storageHook == null)
return false;
boolean autoStart = storageHook.isAutoStart();
String[] autoStartExceptions = storageHook.getAutoStartExceptions();
// no exceptions, it is easy to figure it out
if (autoStartExceptions == null)
return autoStart;
// otherwise, we need to check if the package is in the exceptions list
int dotPosition = className.lastIndexOf('.');
// the class has no package name... no exceptions apply
if (dotPosition == -1)
return autoStart;
String packageName = className.substring(0, dotPosition);
// should activate if autoStart and package is not an exception, or if !autoStart and package is exception
return autoStart ^ contains(autoStartExceptions, packageName);
}
private boolean contains(String[] array, String element) {
for (int i = 0; i < array.length; i++)
if (array[i].equals(element))
return true;
return false;
}
private BundleStopper getBundleStopper(BaseData bundledata) {
AdaptorHook[] adaptorhooks = bundledata.getAdaptor().getHookRegistry().getAdaptorHooks();
for (int i = 0; i < adaptorhooks.length; i++)
if (adaptorhooks[i] instanceof EclipseAdaptorHook)
return ((EclipseAdaptorHook) adaptorhooks[i]).getBundleStopper();
return null;
}
public void addHooks(HookRegistry hookRegistry) {
hookRegistry.addClassLoadingStatsHook(this);
}
}