blob: 26a76e6ed0841514ec8f8fba9939b86d57767742 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2011 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.pde.core.target;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.TargetPlatform;
import org.eclipse.pde.internal.core.*;
import org.eclipse.pde.internal.core.target.*;
/**
* Sets the current target platform based on a target definition.
*
* TODO 159072 Delete? Move to part of the service?
*
* @since 3.8
*/
public class LoadTargetDefinitionJob extends WorkspaceJob {
private static final String JOB_FAMILY_ID = "LoadTargetDefinitionJob"; //$NON-NLS-1$
/**
* Target definition being loaded
*/
private ITargetDefinition fTarget;
/**
* Whether a target definition was specified
*/
private boolean fNone = false;
/**
* Constructs a new operation to load the specified target definition
* as the current target platform. When <code>null</code> is specified
* the target platform is empty and all other settings are default. This
* method will cancel all existing LoadTargetDefinitionJob instances then
* schedules the operation as a user job.
*
* @param target target definition or <code>null</code> if none
*/
public static void load(ITargetDefinition target) {
load(target, null);
}
/**
* Constructs a new operation to load the specified target definition
* as the current target platform. When <code>null</code> is specified
* the target platform is empty and all other settings are default. This
* method will cancel all existing LoadTargetDefinitionJob instances then
* schedules the operation as a user job. Adds the given listener to the
* job that is started.
*
* @param target target definition or <code>null</code> if none
* @param listener job change listener that will be added to the created job
*/
public static void load(ITargetDefinition target, IJobChangeListener listener) {
Job.getJobManager().cancel(JOB_FAMILY_ID);
Job job = new LoadTargetDefinitionJob(target);
job.setUser(true);
if (listener != null) {
job.addJobChangeListener(listener);
}
job.schedule();
}
/**
* Constructs a new operation to load the specified target definition
* as the current target platform. When <code>null</code> is specified
* the target platform is empty and all other settings are default.
*<p>
* Clients should use {@link #getLoadJob(ITargetDefinition)} instead to ensure
* any existing jobs are cancelled.
* </p>
* @param target target definition or <code>null</code> if none
*/
public LoadTargetDefinitionJob(ITargetDefinition target) {
super(Messages.LoadTargetDefinitionJob_0);
fTarget = target;
if (target == null) {
fNone = true;
ITargetPlatformService service = (ITargetPlatformService) PDECore.getDefault().acquireService(ITargetPlatformService.class.getName());
fTarget = service.newTarget();
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
*/
public boolean belongsTo(Object family) {
return JOB_FAMILY_ID.equals(family);
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.WorkspaceJob#runInWorkspace(org.eclipse.core.runtime.IProgressMonitor)
*/
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
try {
PDEPreferencesManager preferences = PDECore.getDefault().getPreferencesManager();
monitor.beginTask(Messages.LoadTargetOperation_mainTaskName, 100);
loadEnvironment(preferences, new SubProgressMonitor(monitor, 5));
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
loadArgs(preferences, new SubProgressMonitor(monitor, 5));
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
loadJRE(preferences, new SubProgressMonitor(monitor, 15));
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
loadImplicitPlugins(preferences, new SubProgressMonitor(monitor, 15));
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
loadPlugins(preferences, new SubProgressMonitor(monitor, 60));
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
loadAdditionalPreferences(preferences);
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
PDECore.getDefault().getPreferencesManager().savePluginPreferences();
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
} finally {
monitor.done();
}
return Status.OK_STATUS;
}
/**
* Configures program and VM argument preferences based on the target
* definition.
*
* @param pref preference manager
* @param monitor progress monitor
*/
private void loadArgs(PDEPreferencesManager pref, IProgressMonitor monitor) {
monitor.beginTask(Messages.LoadTargetOperation_argsTaskName, 2);
String args = fTarget.getProgramArguments();
pref.setValue(ICoreConstants.PROGRAM_ARGS, (args != null) ? args : ""); //$NON-NLS-1$
monitor.worked(1);
args = fTarget.getVMArguments();
pref.setValue(ICoreConstants.VM_ARGS, (args != null) ? args : ""); //$NON-NLS-1$
monitor.done();
}
/**
* Configures the environment preferences from the target definition.
*
* @param pref preference manager
* @param monitor progress monitor
*/
private void loadEnvironment(PDEPreferencesManager pref, IProgressMonitor monitor) {
monitor.beginTask(Messages.LoadTargetOperation_envTaskName, 1);
setEnvironmentPref(pref, ICoreConstants.ARCH, fTarget.getArch());
setEnvironmentPref(pref, ICoreConstants.NL, fTarget.getNL());
setEnvironmentPref(pref, ICoreConstants.OS, fTarget.getOS());
setEnvironmentPref(pref, ICoreConstants.WS, fTarget.getWS());
monitor.done();
}
/**
* Sets the given preference to default when <code>null</code> or the
* specified value.
*
* @param pref preference manager
* @param key preference key
* @param value preference value or <code>null</code>
*/
private void setEnvironmentPref(PDEPreferencesManager pref, String key, String value) {
if (value == null) {
pref.setToDefault(key);
} else {
pref.setValue(key, value);
}
}
/**
* Sets the workspace default JRE based on the target's JRE container.
*
* @param pref
* @param monitor
*/
private void loadJRE(PDEPreferencesManager pref, IProgressMonitor monitor) {
IPath container = fTarget.getJREContainer();
monitor.beginTask(Messages.LoadTargetOperation_jreTaskName, 1);
if (container != null) {
IVMInstall jre = JavaRuntime.getVMInstall(container);
if (jre != null) {
IVMInstall def = JavaRuntime.getDefaultVMInstall();
if (!jre.equals(def)) {
try {
JavaRuntime.setDefaultVMInstall(jre, null);
} catch (CoreException e) {
}
}
}
}
monitor.done();
}
/**
* Sets implicit dependencies, if any
*
* @param pref preference store
* @param monitor progress monitor
*/
private void loadImplicitPlugins(PDEPreferencesManager pref, IProgressMonitor monitor) {
NameVersionDescriptor[] infos = fTarget.getImplicitDependencies();
if (infos != null) {
monitor.beginTask(Messages.LoadTargetOperation_implicitPluginsTaskName, infos.length + 1);
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < infos.length; i++) {
buffer.append(infos[i].getId()).append(',');
monitor.worked(1);
}
if (infos.length > 0)
buffer.setLength(buffer.length() - 1);
pref.setValue(ICoreConstants.IMPLICIT_DEPENDENCIES, buffer.toString());
}
monitor.done();
}
/**
* Resolves the bundles in the target platform and sets them in the corresponding
* CHECKED_PLUGINS preference. Sets home and addition location preferences as well.
*
* @param pref
* @param monitor
* @throws CoreException
*/
private void loadPlugins(PDEPreferencesManager pref, IProgressMonitor monitor) throws CoreException {
monitor.beginTask(Messages.LoadTargetOperation_loadPluginsTaskName, 100);
String currentPath = pref.getString(ICoreConstants.PLATFORM_PATH);
ITargetLocation[] containers = fTarget.getTargetLocations();
// the first container is assumed to be the primary/home location
String path = null;
if (containers != null && containers.length > 0) {
path = ((AbstractBundleContainer) containers[0]).getLocation(true);
}
if (path == null) {
path = TargetPlatform.getDefaultLocation();
} else {
try {
IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager();
path = manager.performStringSubstitution(path);
} catch (CoreException e) {
return;
}
}
monitor.worked(10);
List additional = getAdditionalLocs();
handleReload(path, additional, pref, new SubProgressMonitor(monitor, 85));
// update preferences (Note: some preferences updated in handleReload())
pref.setValue(ICoreConstants.PLATFORM_PATH, path);
String mode = new Path(path).equals(new Path(TargetPlatform.getDefaultLocation())) ? ICoreConstants.VALUE_USE_THIS : ICoreConstants.VALUE_USE_OTHER;
pref.setValue(ICoreConstants.TARGET_MODE, mode);
ListIterator li = additional.listIterator();
StringBuffer buffer = new StringBuffer();
while (li.hasNext())
buffer.append(li.next()).append(","); //$NON-NLS-1$
if (buffer.length() > 0)
buffer.setLength(buffer.length() - 1);
pref.setValue(ICoreConstants.ADDITIONAL_LOCATIONS, buffer.toString());
String newValue = currentPath;
for (int i = 0; i < 4; i++) {
String value = pref.getString(ICoreConstants.SAVED_PLATFORM + i);
pref.setValue(ICoreConstants.SAVED_PLATFORM + i, newValue);
if (!value.equals(currentPath))
newValue = value;
else
break;
}
monitor.done();
}
/**
* Sets the TARGET_PROFILE preference which stores the ID of the target profile used
* (if based on an target extension) or the workspace location of the file that
* was used. For now we just clear it.
* <p>
* Sets the WORKSPACE_TARGET_HANDLE.
* </p>
* @param pref
*/
private void loadAdditionalPreferences(PDEPreferencesManager pref) throws CoreException {
pref.setValue(ICoreConstants.TARGET_PROFILE, ""); //$NON-NLS-1$
String memento = fTarget.getHandle().getMemento();
if (fNone) {
memento = ICoreConstants.NO_TARGET;
}
pref.setValue(ICoreConstants.WORKSPACE_TARGET_HANDLE, memento);
ITargetLocation[] containers = fTarget.getTargetLocations();
boolean profile = false;
if (containers != null && containers.length > 0) {
profile = containers[0] instanceof ProfileBundleContainer;
}
pref.setValue(ICoreConstants.TARGET_PLATFORM_REALIZATION, profile);
}
/**
* Returns a list of additional locations of bundles.
*
* @return additional bundle locations
*/
private List getAdditionalLocs() throws CoreException {
ArrayList additional = new ArrayList();
// secondary containers are considered additional
ITargetLocation[] containers = fTarget.getTargetLocations();
if (containers != null && containers.length > 1) {
for (int i = 1; i < containers.length; i++) {
additional.add(((AbstractBundleContainer) containers[i]).getLocation(true));
}
}
return additional;
}
private void handleReload(String targetLocation, List additionalLocations, PDEPreferencesManager pref, IProgressMonitor monitor) throws CoreException {
SubMonitor subMon = SubMonitor.convert(monitor, Messages.LoadTargetOperation_reloadTaskName, 100);
try {
Set included = new HashSet();
Set duplicates = new HashSet();
List infos = new ArrayList();
Set includedIds = new HashSet();
if (!fTarget.isResolved()) {
// Even if there are errors in the target, don't interrupt the user with an error dialog
fTarget.resolve(subMon.newChild(20));
} else {
subMon.worked(20);
}
if (subMon.isCanceled()) {
return;
}
// If there are problems resolving, values may be null, set preferences as though target is empty (bug 347668)
TargetBundle[] includedBundles = fTarget.getBundles();
if (includedBundles == null) {
includedBundles = new TargetBundle[0];
}
TargetBundle[] allBundles = fTarget.getAllBundles();
if (allBundles == null) {
allBundles = new TargetBundle[0];
}
TargetFeature[] allFeatures = fTarget.getAllFeatures();
if (allFeatures == null) {
allFeatures = new TargetFeature[0];
}
// collect all bundles, ignoring duplicates (symbolic name & version)
List pooled = new ArrayList();
boolean considerPool = false;
for (int i = 0; i < includedBundles.length; i++) {
if (includedBundles[i].getStatus().isOK()) {
BundleInfo bundleInfo = includedBundles[i].getBundleInfo();
NameVersionDescriptor desc = new NameVersionDescriptor(bundleInfo.getSymbolicName(), bundleInfo.getVersion());
File file = new File(bundleInfo.getLocation());
boolean inPool = P2TargetUtils.BUNDLE_POOL.isPrefixOf(new Path(file.getAbsolutePath()));
considerPool = considerPool || inPool;
if (!duplicates.contains(desc)) {
if (inPool) {
pooled.add(file);
}
infos.add(bundleInfo);
included.add(bundleInfo);
includedIds.add(bundleInfo.getSymbolicName());
duplicates.add(desc);
}
}
}
// Compute missing (not included) bundles (preference need to know disabled/missing bundles)
List missing = new ArrayList();
NameVersionDescriptor[] restrictions = fTarget.getIncluded();
if (restrictions != null) {
for (int j = 0; j < allBundles.length; j++) {
TargetBundle bi = allBundles[j];
if (!included.contains(bi.getBundleInfo())) {
missing.add(bi.getBundleInfo());
}
}
}
List paths = new ArrayList(infos.size() + missing.size());
Iterator iterator = infos.iterator();
while (iterator.hasNext()) {
BundleInfo info = (BundleInfo) iterator.next();
try {
paths.add(new File(info.getLocation()).toURL());
} catch (MalformedURLException e) {
throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, Messages.LoadTargetDefinitionJob_1, e));
}
}
// generate URLs and save CHECKED_PLUGINS (which are missing), and add to master list of paths
StringBuffer checked = new StringBuffer();
StringBuffer versions = new StringBuffer();
int count = 0;
iterator = missing.iterator();
Set missingDescriptions = new HashSet(missing.size());
while (iterator.hasNext()) {
BundleInfo bi = (BundleInfo) iterator.next();
NameVersionDescriptor desc = new NameVersionDescriptor(bi.getSymbolicName(), bi.getVersion());
missingDescriptions.add(desc);
try {
paths.add(new File(bi.getLocation()).toURL());
} catch (MalformedURLException e) {
throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, Messages.LoadTargetDefinitionJob_1, e));
}
if (count > 0) {
checked.append(" "); //$NON-NLS-1$
}
checked.append(bi.getSymbolicName());
count++;
if (includedIds.contains(bi.getSymbolicName())) {
// multiple versions of the bundle are available and some are included - store version info of excluded bundles
if (versions.length() > 0) {
versions.append(" "); //$NON-NLS-1$
}
versions.append(desc.toPortableString());
}
}
URL[] urls = (URL[]) paths.toArray(new URL[paths.size()]);
PDEState state = new PDEState(urls, true, new SubProgressMonitor(monitor, 45));
IPluginModelBase[] models = state.getTargetModels();
for (int i = 0; i < models.length; i++) {
NameVersionDescriptor nv = new NameVersionDescriptor(models[i].getPluginBase().getId(), models[i].getPluginBase().getVersion());
models[i].setEnabled(!missingDescriptions.contains(nv));
}
if (subMon.isCanceled()) {
return;
}
// save CHECKED_PLUGINS
if (urls.length == 0) {
pref.setValue(ICoreConstants.CHECKED_PLUGINS, ICoreConstants.VALUE_SAVED_NONE);
} else if (missing.size() == 0) {
pref.setValue(ICoreConstants.CHECKED_PLUGINS, ICoreConstants.VALUE_SAVED_ALL);
} else {
pref.setValue(ICoreConstants.CHECKED_PLUGINS, checked.toString());
}
// save CHECKED_VERSION_PLUGINS
if (versions.length() > 0) {
pref.setValue(ICoreConstants.CHECKED_VERSION_PLUGINS, versions.toString());
} else {
// no version information required
pref.setValue(ICoreConstants.CHECKED_VERSION_PLUGINS, ICoreConstants.VALUE_SAVED_NONE);
}
// saved POOLED_BUNDLES
if (pooled.isEmpty()) {
if (considerPool) {
// all pooled bundles are excluded
pref.setValue(ICoreConstants.POOLED_URLS, ICoreConstants.VALUE_SAVED_NONE);
} else {
// nothing in the pool
pref.setValue(ICoreConstants.POOLED_URLS, ""); //$NON-NLS-1$
}
} else {
StringBuffer buf = new StringBuffer();
Iterator iterator2 = pooled.iterator();
while (iterator2.hasNext()) {
File bundle = (File) iterator2.next();
buf.append(bundle.getName()); // only store file name to make workspace portable
if (iterator2.hasNext()) {
buf.append(',');
}
}
pref.setValue(ICoreConstants.POOLED_URLS, buf.toString());
pref.setValue(ICoreConstants.POOLED_BUNDLES, ""); // NO LONGER USED //$NON-NLS-1$
}
// Save the feature list for the external feature model manager to EXTERNAL_FEATURES
StringBuffer featureList = new StringBuffer();
// If the target has includes, but only plug-ins are specified, just include all features
// If the target has feature includes, only add features that are included (bug 308693)
NameVersionDescriptor[] includes = fTarget.getIncluded();
boolean featuresFound = false; // If only plug-ins are specified, include all features
if (includes != null) {
for (int i = 0; i < includes.length; i++) {
if (includes[i].getType() == NameVersionDescriptor.TYPE_FEATURE) {
featuresFound = true;
TargetFeature bestMatch = null;
for (int j = 0; j < allFeatures.length; j++) {
TargetFeature feature = allFeatures[j];
if (feature.getId().equals(includes[i].getId())) {
if (includes[i].getVersion() != null) {
// Try to find an exact feature match
if (includes[i].getVersion().equals(feature.getVersion())) {
// Exact match
bestMatch = feature;
break;
}
} else if (bestMatch != null) {
// If no version specified take the highest version
Version v1 = Version.parseVersion(feature.getVersion());
Version v2 = Version.parseVersion(feature.getVersion());
if (v1.compareTo(v2) > 0) {
bestMatch = feature;
}
}
if (bestMatch == null) {
// If we can't find a version match, just take any name match
bestMatch = feature;
}
}
}
if (bestMatch != null) {
if (featureList.length() > 0) {
featureList.append(',');
}
featureList.append(bestMatch.getId());
featureList.append('@');
featureList.append(bestMatch.getVersion());
}
}
}
}
if (includes == null || !featuresFound) {
// Add all features to the list
for (int i = 0; i < allFeatures.length; i++) {
featureList.append(allFeatures[i].getId());
featureList.append('@');
featureList.append(allFeatures[i].getVersion());
if (i < allFeatures.length - 1) {
featureList.append(',');
}
}
}
pref.setValue(ICoreConstants.EXTERNAL_FEATURES, featureList.toString());
Job job = new TargetPlatformResetJob(state);
job.schedule();
try {
job.join();
} catch (InterruptedException e) {
}
} finally {
if (monitor != null) {
monitor.done();
}
subMon.done();
}
}
}