blob: f261e36b852a1a1b5231b42e934c35d410011e91 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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
* Cloudsmith - https://bugs.eclipse.org/bugs/show_bug.cgi?id=226401
*******************************************************************************/
package org.eclipse.equinox.internal.p2.director.app;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.equinox.internal.p2.console.ProvisioningHelper;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.provisional.p2.director.*;
import org.eclipse.equinox.internal.provisional.p2.engine.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.InstallableUnitQuery;
import org.eclipse.equinox.internal.provisional.p2.query.Collector;
import org.eclipse.osgi.service.resolver.VersionRange;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.packageadmin.PackageAdmin;
public class Application implements IApplication {
private static final Integer EXIT_ERROR = new Integer(13);
static private String FLAVOR_DEFAULT = "tooling"; //$NON-NLS-1$
static private String EXEMPLARY_SETUP = "org.eclipse.equinox.p2.exemplarysetup"; //$NON-NLS-1$
static private String FRAMEWORKADMIN_EQUINOX = "org.eclipse.equinox.frameworkadmin.equinox"; //$NON-NLS-1$
static private String SIMPLE_CONFIGURATOR_MANIPULATOR = "org.eclipse.equinox.simpleconfigurator.manipulator"; //$NON-NLS-1$
public static final int COMMAND_INSTALL = 0;
public static final int COMMAND_UNINSTALL = 1;
public static final int COMMAND_LIST = 2;
public static final String[] COMMAND_NAMES = {"-installIU", "-uninstallIU", "-list"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// private ServiceRegistration directorRegistration;
// private ServiceRegistration plannerRegistration;
// private ServiceRegistration engineRegistration;
// private ServiceRegistration busRegistration;
// private ServiceRegistration metadataManagerRegistration;
// private ServiceRegistration artifactManagerRegistration;
// private IProvisioningEventBus bus;
private Path destination;
private URL[] artifactRepositoryLocations;
private URL[] metadataRepositoryLocations;
private String root;
private Version version = null;
private String flavor;
private String profileId;
private String profileProperties; // a comma-separated list of property pairs "tag=value"
private String bundlePool = null;
private String nl;
private String os;
private String arch;
private String ws;
private boolean roamingProfile = false;
private IPlanner planner;
private IEngine engine;
private int command = -1;
private ServiceReference packageAdminRef;
private PackageAdmin packageAdmin;
private boolean needsToUpdateRoamingValues = false;
private void ambigousCommand(int cmd1, int cmd2) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Ambigous_Command, new Object[] {COMMAND_NAMES[cmd1], COMMAND_NAMES[cmd2]})));
}
private ProfileChangeRequest buildProvisioningRequest(IProfile profile, Collector roots, boolean install) {
ProfileChangeRequest request = new ProfileChangeRequest(profile);
markRoots(request, roots);
if (install) {
request.addInstallableUnits((IInstallableUnit[]) roots.toArray(IInstallableUnit.class));
} else {
request.removeInstallableUnits((IInstallableUnit[]) roots.toArray(IInstallableUnit.class));
}
return request;
}
synchronized Bundle getBundle(String symbolicName) {
if (packageAdmin == null)
return null;
Bundle[] bundles = packageAdmin.getBundles(symbolicName, null);
if (bundles == null)
return null;
//Return the first bundle that is not installed or uninstalled
for (int i = 0; i < bundles.length; i++) {
if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
return bundles[i];
}
}
return null;
}
private String getEnvironmentProperty() {
Properties values = new Properties();
if (os != null)
values.put("osgi.os", os); //$NON-NLS-1$
if (nl != null)
values.put("osgi.nl", nl); //$NON-NLS-1$
if (ws != null)
values.put("osgi.ws", ws); //$NON-NLS-1$
if (arch != null)
values.put("osgi.arch", arch); //$NON-NLS-1$
if (values.isEmpty())
return null;
return toString(values);
}
private IProfile initializeProfile() throws CoreException {
if (profileId == null)
profileId = IProfileRegistry.SELF;
IProfile profile = ProvisioningHelper.getProfile(profileId);
if (profile != null && (ProvisioningHelper.getProfile(IProfileRegistry.SELF) != null && !profile.getProfileId().equals(ProvisioningHelper.getProfile(IProfileRegistry.SELF).getProfileId())))
needsToUpdateRoamingValues = true;
if (profile == null) {
if (destination == null)
missingArgument("destination"); //$NON-NLS-1$
if (flavor == null)
flavor = System.getProperty("eclipse.p2.configurationFlavor", FLAVOR_DEFAULT); //$NON-NLS-1$
Properties props = new Properties();
props.setProperty(IProfile.PROP_INSTALL_FOLDER, destination.toOSString());
props.setProperty(IProfile.PROP_FLAVOR, flavor);
if (bundlePool != null)
if (bundlePool.equals(Messages.destination_commandline))
props.setProperty(IProfile.PROP_CACHE, destination.toOSString());
else
props.setProperty(IProfile.PROP_CACHE, bundlePool);
if (roamingProfile)
props.setProperty(IProfile.PROP_ROAMING, Boolean.TRUE.toString());
String env = getEnvironmentProperty();
if (env != null)
props.setProperty(IProfile.PROP_ENVIRONMENTS, env);
if (profileProperties != null) {
putProperties(profileProperties, props);
}
profile = ProvisioningHelper.addProfile(profileId, props);
String currentFlavor = profile.getProperty(IProfile.PROP_FLAVOR);
if (currentFlavor != null && !currentFlavor.endsWith(flavor))
throw new RuntimeException(Messages.Inconsistent_flavor);
}
return profile;
}
private void initializeRepositories(boolean throwException) throws CoreException {
if (artifactRepositoryLocations == null) {
if (throwException)
missingArgument("artifactRepository"); //$NON-NLS-1$
} else {
for (int i = 0; i < artifactRepositoryLocations.length; i++)
ProvisioningHelper.addArtifactRepository(artifactRepositoryLocations[i]);
}
if (metadataRepositoryLocations == null) {
if (throwException)
missingArgument("metadataRepository"); //$NON-NLS-1$
} else {
for (int i = 0; i < metadataRepositoryLocations.length; i++)
ProvisioningHelper.addMetadataRepository(metadataRepositoryLocations[i]);
}
}
private void initializeServices() {
IDirector director = (IDirector) ServiceHelper.getService(Activator.getContext(), IDirector.class.getName());
if (director == null)
throw new RuntimeException(Messages.Missing_director);
planner = (IPlanner) ServiceHelper.getService(Activator.getContext(), IPlanner.class.getName());
if (planner == null)
throw new RuntimeException(Messages.Missing_planner);
engine = (IEngine) ServiceHelper.getService(Activator.getContext(), IEngine.SERVICE_NAME);
if (engine == null)
throw new RuntimeException(Messages.Missing_Engine);
}
private void markRoots(ProfileChangeRequest request, Collector roots) {
for (Iterator iterator = roots.iterator(); iterator.hasNext();) {
request.setInstallableUnitProfileProperty((IInstallableUnit) iterator.next(), IInstallableUnit.PROP_PROFILE_ROOT_IU, Boolean.TRUE.toString());
}
}
private void missingArgument(String argumentName) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Missing_Required_Argument, argumentName)));
}
private IStatus planAndExecute(IProfile profile, ProvisioningContext context, ProfileChangeRequest request) {
ProvisioningPlan result;
IStatus operationStatus;
result = planner.getProvisioningPlan(request, context, new NullProgressMonitor());
if (!result.getStatus().isOK())
operationStatus = result.getStatus();
else {
operationStatus = engine.perform(profile, new DefaultPhaseSet(), result.getOperands(), context, new NullProgressMonitor());
}
return operationStatus;
}
private void printRequest(ProfileChangeRequest request) {
IInstallableUnit[] toAdd = request.getAddedInstallableUnits();
IInstallableUnit[] toRemove = request.getRemovedInstallableUnits();
for (int i = 0; i < toAdd.length; i++) {
System.out.println(NLS.bind(Messages.Installing, toAdd[i].getId(), toAdd[i].getVersion()));
}
for (int i = 0; i < toRemove.length; i++) {
System.out.println(NLS.bind(Messages.Uninstalling, toRemove[i].getId(), toRemove[i].getVersion()));
}
}
public void processArguments(String[] args) throws Exception {
if (args == null)
return;
for (int i = 0; i < args.length; i++) {
String opt = args[i];
if (opt.equals("-roaming")) { //$NON-NLS-1$
roamingProfile = true;
}
if (opt.equals(COMMAND_NAMES[COMMAND_LIST])) {
if (command != -1)
ambigousCommand(COMMAND_LIST, command);
command = COMMAND_LIST;
}
// check for args without parameters (i.e., a flag arg)
// check for args with parameters. If we are at the last
// argument or
// if the next one
// has a '-' as the first character, then we can't have an arg
// with
// a parm so continue.
if (i == args.length - 1 || args[i + 1].startsWith("-")) //$NON-NLS-1$
continue;
String arg = args[++i];
if (opt.equalsIgnoreCase("-profile")) //$NON-NLS-1$
profileId = arg;
if (opt.equalsIgnoreCase("-profileProperties") || opt.equalsIgnoreCase("-props")) //$NON-NLS-1$ //$NON-NLS-2$
profileProperties = arg;
// we create a path object here to handle ../ entries in the middle of paths
if (opt.equalsIgnoreCase("-destination") || opt.equalsIgnoreCase("-dest")) //$NON-NLS-1$ //$NON-NLS-2$
destination = new Path(arg);
// we create a path object here to handle ../ entries in the middle of paths
if (opt.equalsIgnoreCase("-bundlepool") || opt.equalsIgnoreCase("-bp")) //$NON-NLS-1$ //$NON-NLS-2$
bundlePool = new Path(arg).toOSString();
if (opt.equalsIgnoreCase("-metadataRepository") || opt.equalsIgnoreCase("-metadataRepositories") || opt.equalsIgnoreCase("-mr")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
metadataRepositoryLocations = getURLs(arg);
if (opt.equalsIgnoreCase("-artifactRepository") || opt.equalsIgnoreCase("-artifactRepositories") || opt.equalsIgnoreCase("-ar")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
artifactRepositoryLocations = getURLs(arg);
if (opt.equalsIgnoreCase("-flavor")) //$NON-NLS-1$
flavor = arg;
if (opt.equalsIgnoreCase(COMMAND_NAMES[COMMAND_INSTALL])) {
if (command != -1)
ambigousCommand(COMMAND_INSTALL, command);
root = arg;
command = COMMAND_INSTALL;
}
if (opt.equalsIgnoreCase("-version")) { //$NON-NLS-1$
version = new Version(arg);
}
if (opt.equalsIgnoreCase(COMMAND_NAMES[COMMAND_UNINSTALL])) {
if (command != -1)
ambigousCommand(COMMAND_UNINSTALL, command);
root = arg;
command = COMMAND_UNINSTALL;
}
if (opt.equalsIgnoreCase("-p2.os")) { //$NON-NLS-1$
os = arg;
}
if (opt.equalsIgnoreCase("-p2.ws")) { //$NON-NLS-1$
ws = arg;
}
if (opt.equalsIgnoreCase("-p2.nl")) { //$NON-NLS-1$
nl = arg;
}
if (opt.equalsIgnoreCase("-p2.arch")) { //$NON-NLS-1$
arch = arg;
}
}
}
/**
* @param pairs a comma separated list of tag=value pairs
* @param properties the collection into which the pairs are put
*/
private void putProperties(String pairs, Properties properties) {
StringTokenizer tok = new StringTokenizer(pairs, ",", true); //$NON-NLS-1$
while (tok.hasMoreTokens()) {
String next = tok.nextToken().trim();
int i = next.indexOf('=');
if (i > 0 && i < next.length() - 1) {
String tag = next.substring(0, i).trim();
String value = next.substring(i + 1, next.length()).trim();
if (tag.length() > 0 && value.length() > 0) {
properties.put(tag, value);
}
}
}
}
public Object run(String[] args) throws Exception {
long time = -System.currentTimeMillis();
initializeServices();
processArguments(args);
IStatus operationStatus = Status.OK_STATUS;
InstallableUnitQuery query;
Collector roots;
switch (command) {
case COMMAND_INSTALL :
case COMMAND_UNINSTALL :
initializeRepositories(command == COMMAND_INSTALL);
IProfile profile = initializeProfile();
query = new InstallableUnitQuery(root, version == null ? VersionRange.emptyRange : new VersionRange(version, true, version, true));
roots = ProvisioningHelper.getInstallableUnits(null, query, new LatestIUVersionCollector(), new NullProgressMonitor());
if (roots.size() <= 0)
roots = profile.query(query, roots, new NullProgressMonitor());
if (roots.size() <= 0) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Missing_IU, root)));
System.out.println(NLS.bind(Messages.Missing_IU, root));
return EXIT_ERROR;
}
if (!updateRoamingProperties(profile).isOK()) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Cant_change_roaming, profile.getProfileId())));
System.out.println(NLS.bind(Messages.Cant_change_roaming, profile.getProfileId()));
return EXIT_ERROR;
}
ProvisioningContext context = new ProvisioningContext();
ProfileChangeRequest request = buildProvisioningRequest(profile, roots, command == COMMAND_INSTALL);
printRequest(request);
operationStatus = planAndExecute(profile, context, request);
break;
case COMMAND_LIST :
query = new InstallableUnitQuery(null, VersionRange.emptyRange);
if (metadataRepositoryLocations == null)
missingArgument("metadataRepository"); //$NON-NLS-1$
for (int i = 0; i < metadataRepositoryLocations.length; i++) {
roots = ProvisioningHelper.getInstallableUnits(metadataRepositoryLocations[i], query, new NullProgressMonitor());
Iterator unitIterator = roots.iterator();
while (unitIterator.hasNext()) {
IInstallableUnit iu = (IInstallableUnit) unitIterator.next();
System.out.println(iu.getId());
}
}
break;
}
time += System.currentTimeMillis();
if (operationStatus.isOK())
System.out.println(NLS.bind(Messages.Operation_complete, new Long(time)));
else {
System.out.println(Messages.Operation_failed);
LogHelper.log(operationStatus);
return EXIT_ERROR;
}
return IApplication.EXIT_OK;
}
private synchronized void setPackageAdmin(PackageAdmin service) {
packageAdmin = service;
}
private boolean startEarly(String bundleName) throws BundleException {
Bundle bundle = getBundle(bundleName);
if (bundle == null)
return false;
bundle.start(Bundle.START_TRANSIENT);
return true;
}
public Object start(IApplicationContext context) throws Exception {
packageAdminRef = Activator.getContext().getServiceReference(PackageAdmin.class.getName());
setPackageAdmin((PackageAdmin) Activator.getContext().getService(packageAdminRef));
if (!startEarly(EXEMPLARY_SETUP)) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Missing_bundle, EXEMPLARY_SETUP)));
return EXIT_ERROR;
}
if (!startEarly(SIMPLE_CONFIGURATOR_MANIPULATOR)) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Missing_bundle, SIMPLE_CONFIGURATOR_MANIPULATOR)));
return EXIT_ERROR;
}
if (!startEarly(FRAMEWORKADMIN_EQUINOX)) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Missing_bundle, FRAMEWORKADMIN_EQUINOX)));
return EXIT_ERROR;
}
return run((String[]) context.getArguments().get("application.args")); //$NON-NLS-1$
}
public void stop() {
setPackageAdmin(null);
Activator.getContext().ungetService(packageAdminRef);
}
private String toString(Properties context) {
StringBuffer result = new StringBuffer();
for (Enumeration iter = context.keys(); iter.hasMoreElements();) {
String key = (String) iter.nextElement();
result.append(key);
result.append('=');
result.append(context.get(key));
if (iter.hasMoreElements())
result.append(',');
}
return result.toString();
}
private IStatus updateRoamingProperties(IProfile profile) {
if (!needsToUpdateRoamingValues)
return Status.OK_STATUS;
ProfileChangeRequest request = new ProfileChangeRequest(profile);
if (!Boolean.valueOf(profile.getProperty(IProfile.PROP_ROAMING)).booleanValue())
return Status.OK_STATUS;
if (!destination.equals(new File(profile.getProperty(IProfile.PROP_INSTALL_FOLDER)))) {
request.setProfileProperty(IProfile.PROP_INSTALL_FOLDER, destination.toOSString());
}
if (!destination.equals(new File(profile.getProperty(IProfile.PROP_CACHE)))) {
request.setProfileProperty(IProfile.PROP_CACHE, destination.toOSString());
}
if (request.getProfileProperties().size() == 0)
return Status.OK_STATUS;
ProvisioningPlan result = planner.getProvisioningPlan(request, new ProvisioningContext(), new NullProgressMonitor());
if (!result.getStatus().isOK())
return result.getStatus();
return engine.perform(profile, new DefaultPhaseSet(), result.getOperands(), new ProvisioningContext(), new NullProgressMonitor());
}
private static URL[] getURLs(String spec) {
if (spec == null)
return null;
String[] urlSpecs = getArrayFromString(spec, ","); //$NON-NLS-1$
ArrayList result = new ArrayList(urlSpecs.length);
for (int i = 0; i < urlSpecs.length; i++) {
try {
result.add(new URL(urlSpecs[i]));
} catch (MalformedURLException e) {
NLS.bind(Messages.Ignored_repo, urlSpecs[i]);
}
}
return (URL[]) result.toArray(new URL[result.size()]);
}
/**
* Convert a list of tokens into an array. The list separator has to be
* specified.
*/
public static String[] getArrayFromString(String list, String separator) {
if (list == null || list.trim().equals("")) //$NON-NLS-1$
return new String[0];
List result = new ArrayList();
for (StringTokenizer tokens = new StringTokenizer(list, separator); tokens.hasMoreTokens();) {
String token = tokens.nextToken().trim();
if (!token.equals("")) //$NON-NLS-1$
result.add(token);
}
return (String[]) result.toArray(new String[result.size()]);
}
}