package org.eclipse.update.configurator; | |
import java.io.File; | |
import java.io.IOException; | |
import java.net.MalformedURLException; | |
import java.net.URL; | |
import java.util.*; | |
import org.eclipse.core.runtime.*; | |
import org.eclipse.osgi.service.environment.DebugOptions; | |
import org.eclipse.osgi.service.environment.EnvironmentInfo; | |
import org.osgi.framework.*; | |
import org.osgi.service.packageadmin.PackageAdmin; | |
import org.osgi.service.startlevel.StartLevel; | |
import org.osgi.util.tracker.ServiceTracker; | |
public class ConfigurationActivator implements BundleActivator { | |
private final static String DEFAULT_CONVERTER = "org.eclipse.update.configurator.migration.PluginConverter"; //$NON-NLS-1$ | |
public static String PI_CONFIGURATOR = "org.eclipse.update.configurator"; | |
// debug options | |
public static String OPTION_DEBUG = PI_CONFIGURATOR + "/debug"; | |
public static String OPTION_DEBUG_CONVERTER = PI_CONFIGURATOR | |
+ "/converter/debug"; | |
// debug values | |
public static boolean DEBUG = false; | |
public static boolean DEBUG_CONVERTER = false; | |
private static BundleContext context; | |
private ServiceTracker platformTracker; | |
private ServiceRegistration configurationFactorySR; | |
private String[] allArgs; | |
// location used to put the generated manfests | |
private String cacheLocation = (String) System.getProperties().get( | |
"osgi.manifest.cache"); //PASCAL Need to set this value somewhere (probably from boot) | |
private IPluginConverter converter; | |
private Set ignore; | |
private BundleListener reconcilerListener; | |
public void start(BundleContext ctx) throws Exception { | |
context = ctx; | |
loadOptions(); | |
if (DEBUG) | |
System.out.println("Starting update configurator..."); | |
computeIgnoredBundles(); | |
loadConverter(); | |
obtainArgs(); | |
installBundles(); | |
} | |
private void computeIgnoredBundles() { | |
String ignoreList = System | |
.getProperty("eclipse.ignore", | |
"org.eclipse.osgi,org.eclipse.core.boot,org.eclipse.core.runtime.adaptor"); | |
ignore = new HashSet(); | |
StringTokenizer tokenizer = new StringTokenizer(ignoreList, ","); | |
while (tokenizer.hasMoreTokens()) | |
ignore.add(tokenizer.nextToken().trim()); | |
} | |
private boolean shouldIgnore(String bundleName) { | |
if (ignore == null) | |
return false; | |
StringTokenizer tokenizer = new StringTokenizer(bundleName, "._"); | |
String partialName = ""; | |
while (tokenizer.hasMoreTokens()) { | |
partialName += tokenizer.nextToken(); | |
if (ignore.contains(partialName)) | |
return true; | |
partialName += "."; | |
} | |
return false; | |
} | |
private void loadConverter() { | |
// TODO look at making this an extension | |
String converterClassName = System.getProperty( | |
"eclipse.manifestconverter", DEFAULT_CONVERTER); | |
if (converterClassName == null) | |
return; | |
Class converterClass; | |
try { | |
converterClass = Class.forName(converterClassName); | |
} catch (ClassNotFoundException e) { | |
return; | |
} | |
try { | |
converter = (IPluginConverter) converterClass.newInstance(); | |
} catch (InstantiationException e1) { | |
return; | |
} catch (IllegalAccessException e1) { | |
return; | |
} catch (ClassCastException cce) { | |
return; | |
} | |
} | |
private void obtainArgs() { | |
// all this is only to get the application args | |
EnvironmentInfo envInfo = null; | |
ServiceReference envInfoSR = context | |
.getServiceReference(EnvironmentInfo.class.getName()); | |
if (envInfoSR != null) | |
envInfo = (EnvironmentInfo) context.getService(envInfoSR); | |
if (envInfo == null) | |
throw new IllegalStateException(); | |
this.allArgs = envInfo.getAllArgs(); | |
// we have what we want - release the service | |
context.ungetService(envInfoSR); | |
} | |
public void stop(BundleContext ctx) throws Exception { | |
releasePlatform(); | |
configurationFactorySR.unregister(); | |
} | |
private void releasePlatform() { | |
if (platformTracker == null) | |
return; | |
platformTracker.close(); | |
platformTracker = null; | |
} | |
private IPlatform acquirePlatform() { | |
if (platformTracker == null) { | |
platformTracker = new ServiceTracker(context, IPlatform.class | |
.getName(), null); | |
platformTracker.open(); | |
} | |
IPlatform result = (IPlatform) platformTracker.getService(); | |
while (result == null) { | |
try { | |
platformTracker.waitForService(1000); | |
result = (IPlatform) platformTracker.getService(); | |
} catch (InterruptedException ie) { | |
} | |
} | |
return result; | |
} | |
private void installBundles() { | |
IPlatform platform = acquirePlatform(); | |
String metaPath = platform.getLocation().append(".metadata").toOSString(); | |
URL installURL = platform.getInstallURL(); | |
ServiceReference reference = context.getServiceReference(StartLevel.class.getName()); | |
StartLevel start = null; | |
if (reference != null) | |
start = (StartLevel) context.getService(reference); | |
try { | |
configurationFactorySR = context.registerService(IPlatformConfigurationFactory.class.getName(), new PlatformConfigurationFactory(), null); | |
PlatformConfiguration config = getPlatformConfiguration(allArgs, metaPath, installURL); | |
URL[] plugins = config.getPluginPath(); | |
ArrayList installed = new ArrayList(plugins.length); | |
for (int i = 0; i < plugins.length; i++) { | |
try { | |
String location = plugins[i].toExternalForm(); | |
checkOrGenerateManifest(location); | |
location = "reference:" + location.substring(0, location.lastIndexOf('/')); | |
if (!isInstalled(location)) { | |
try { | |
Bundle target = context.installBundle(location); | |
installed.add(target); | |
if (start != null) | |
start.setBundleStartLevel(target, 4); | |
} catch (Exception e) { | |
System.err.println("Ignoring bundle at: " + location); | |
System.err.println(e.getMessage()); | |
} | |
} | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
context.ungetService(reference); | |
refreshPackages((Bundle[]) installed.toArray(new Bundle[installed.size()])); | |
if (System.getProperty("eclipse.application") == null || System.getProperty("eclipse.application").equals(PlatformConfiguration.RECONCILER_APP)) | |
System.setProperty("eclipse.application", config.getApplicationIdentifier()); | |
// if (config.getApplicationIdentifier().equals(PlatformConfiguration.RECONCILER_APP) ) { | |
// reconcilerListener = reconcilerListener(); | |
// context.addBundleListener(reconcilerListener); | |
// } | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} finally { | |
releasePlatform(); | |
} | |
} | |
private BundleListener reconcilerListener() { | |
return new BundleListener() { | |
public void bundleChanged(BundleEvent event) { | |
String buid = event.getBundle().getUniqueId(); | |
if (event.getType() == BundleEvent.STOPPED && buid != null | |
&& buid.equals("org.eclipse.update.core")) | |
runPostReconciler(); | |
} | |
}; | |
} | |
private void runPostReconciler() { | |
Runnable postReconciler = new Runnable() { | |
public void run() { | |
try { | |
Bundle apprunner = context | |
.getBundle("org.eclipse.core.applicationrunner"); | |
apprunner.stop(); | |
context.removeBundleListener(reconcilerListener); | |
try { | |
PlatformConfiguration.shutdown(); | |
} catch (IOException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
installBundles(); | |
apprunner.start(); | |
} catch (BundleException be) { | |
be.printStackTrace(); | |
} | |
} | |
}; | |
new Thread(postReconciler, "Post reconciler").start(); | |
} | |
/** | |
* @param location | |
*/ | |
private void checkOrGenerateManifest(String pluginManifestLocationURL) { | |
if (converter == null) | |
return; | |
String pluginManifestLocation = null; | |
try { | |
pluginManifestLocation = new URL(pluginManifestLocationURL) | |
.getPath(); | |
} catch (MalformedURLException e) { | |
return; | |
} | |
File pluginDir = new File(pluginManifestLocation).getParentFile(); | |
if (shouldIgnore(pluginDir.getName())) | |
return; | |
File manifest = new File(pluginDir, "META-INF/MANIFEST.MF"); | |
if (manifest.exists()) | |
return; | |
// bail if the install location is not writable and we don't know where else to write to | |
if (cacheLocation == null) | |
return; | |
File generationLocation = new File(cacheLocation, | |
computeFileName(pluginDir.getPath()) + ".MF"); | |
if (generationLocation.exists()) | |
return; | |
if (!converter.convertManifest(pluginDir, generationLocation)) | |
System.out.println(pluginDir + " manifest generation failed"); | |
} | |
/* | |
* Derives a file name corresponding to a path: | |
* c:\autoexec.bat -> c__autoexec.bat | |
*/ | |
private String computeFileName(String filePath) { | |
StringBuffer newName = new StringBuffer(filePath); | |
for (int i = 0; i < filePath.length(); i++) { | |
char c = newName.charAt(i); | |
if (c == ':' || c == '/' || c == '\\') | |
newName.setCharAt(i, '_'); | |
} | |
return newName.toString(); | |
} | |
/** | |
* This is a major hack to try to get the reconciler application running. However we should find a way to not run it. | |
* @param args | |
* @param metaPath | |
* @return | |
*/ | |
private PlatformConfiguration getPlatformConfiguration(String[] args, | |
String metaPath, URL installURL) { | |
try { | |
PlatformConfiguration.startup(args, null, null, metaPath, | |
installURL); | |
} catch (Exception e) { | |
if (platformTracker != null) { | |
String message = e.getMessage(); | |
if (message == null) | |
message = ""; | |
IStatus status = new Status(IStatus.ERROR, | |
IPlatform.PI_RUNTIME, IStatus.OK, message, e); | |
((IPlatform) platformTracker.getService()).getLog( | |
context.getBundle()).log(status); | |
} | |
} | |
return PlatformConfiguration.getCurrent(); | |
} | |
/** | |
* Do PackageAdmin.refreshPackages() in a synchronous way. After installing | |
* all the requested bundles we need to do a refresh and want to ensure that | |
* everything is done before returning. | |
* @param bundles | |
*/ | |
private void refreshPackages(Bundle[] bundles) { | |
if (bundles.length == 0) | |
return; | |
ServiceReference packageAdminRef = context | |
.getServiceReference(PackageAdmin.class.getName()); | |
PackageAdmin packageAdmin = null; | |
if (packageAdminRef != null) { | |
packageAdmin = (PackageAdmin) context.getService(packageAdminRef); | |
if (packageAdmin == null) | |
return; | |
} | |
// TODO this is such a hack it is silly. There are still cases for race conditions etc | |
// but this should allow for some progress... | |
final Object semaphore = new Object(); | |
FrameworkListener listener = new FrameworkListener() { | |
public void frameworkEvent(FrameworkEvent event) { | |
if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) | |
synchronized (semaphore) { | |
semaphore.notifyAll(); | |
} | |
} | |
}; | |
context.addFrameworkListener(listener); | |
packageAdmin.refreshPackages(bundles); | |
synchronized (semaphore) { | |
try { | |
semaphore.wait(); | |
} catch (InterruptedException e) { | |
} | |
} | |
context.removeFrameworkListener(listener); | |
context.ungetService(packageAdminRef); | |
} | |
private boolean isInstalled(String location) { | |
Bundle[] installed = context.getBundles(); | |
for (int i = 0; i < installed.length; i++) { | |
Bundle bundle = installed[i]; | |
if (location.equalsIgnoreCase(bundle.getLocation())) | |
return true; | |
} | |
return false; | |
} | |
private void loadOptions() { | |
// all this is only to get the application args | |
DebugOptions service = null; | |
ServiceReference reference = context | |
.getServiceReference(DebugOptions.class.getName()); | |
if (reference != null) | |
service = (DebugOptions) context.getService(reference); | |
if (service == null) | |
return; | |
try { | |
DEBUG = service.getBooleanOption(OPTION_DEBUG, false); | |
if (!DEBUG) | |
return; | |
DEBUG_CONVERTER = service.getBooleanOption(OPTION_DEBUG_CONVERTER, | |
false); | |
} finally { | |
// we have what we want - release the service | |
context.ungetService(reference); | |
} | |
} | |
public static BundleContext getBundleContext() { | |
return context; | |
} | |
} |