/*******************************************************************************
* Copyright (c) 2009 Paul VanderLei, Simon Archer, Jeff McAffer and others. All
* rights reserved. This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 and Eclipse Distribution License
* v1.0 which accompanies this distribution. The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution License
* is available at http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Paul VanderLei, Simon Archer, Jeff McAffer - initial API and implementation
*******************************************************************************/
package org.eclipse.examples.toast.internal.backend.provisioning;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.IProvisioningAgentProvider;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.engine.IEngine;
import org.eclipse.equinox.p2.engine.IProfile;
import org.eclipse.equinox.p2.engine.IProfileRegistry;
import org.eclipse.equinox.p2.engine.IProvisioningPlan;
import org.eclipse.equinox.p2.engine.ProvisioningContext;
import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.ITouchpointType;
import org.eclipse.equinox.p2.metadata.MetadataFactory;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.planner.IPlanner;
import org.eclipse.equinox.p2.planner.IProfileChangeRequest;
import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.query.QueryUtil;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
import org.eclipse.examples.toast.backend.provisioning.IProvisioner;
import org.eclipse.examples.toast.core.ICoreConstants;
import org.eclipse.examples.toast.core.LogUtility;
import org.eclipse.examples.toast.core.PropertyManager;
public class Provisioner implements IProvisioner {
private static final String PROP_TOAST_ROOT = "toast.root"; //$NON-NLS-1$
private static final String PROFILE_LOCATION = "toast.profile.location"; //$NON-NLS-1$
private static final String REPO_LOCATIONS = "toast.repos"; //$NON-NLS-1$
private URI dataLocation;
private IProvisioningAgentProvider agentProvider;
private IProvisioningAgent agent;
private IProfileRegistry registry;
private IArtifactRepositoryManager artifactManager;
private IMetadataRepositoryManager metadataManager;
private IPlanner planner;
private IEngine engine;
public Provisioner() {
}
protected void setAgentProvider(IProvisioningAgentProvider value) {
agentProvider = value;
}
protected void startup() {
String location = PropertyManager.getProperty(PROFILE_LOCATION);
if (location != null)
try {
dataLocation = new URI(location);
} catch (URISyntaxException e) {
// do nothing. will deal with it below
}
if (dataLocation == null) {
LogUtility.logError("Invalid profile location URI - " + location);
return;
}
try {
agent = agentProvider.createAgent(dataLocation);
} catch (ProvisionException e) {
LogUtility.logError("Unable to initialize p2 agent", e);
return;
}
registry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
metadataManager = (IMetadataRepositoryManager) agent.getService(IMetadataRepositoryManager.SERVICE_NAME);
artifactManager = (IArtifactRepositoryManager) agent.getService(IArtifactRepositoryManager.SERVICE_NAME);
planner = (IPlanner) agent.getService(IPlanner.SERVICE_NAME);
engine = (IEngine) agent.getService(IEngine.SERVICE_NAME);
String spec = PropertyManager.getProperty(REPO_LOCATIONS);
if (spec != null)
addRepositories(spec);
}
protected void shutdown() {
}
private IMetadataRepository[] addRepositories(String spec) {
String[] locations = spec.split(",");
ArrayList result = new ArrayList();
for (int i = 0; i < locations.length; i++) {
URI location = null;
try {
location = new URI(locations[i].trim());
metadataManager.loadRepository(location, null);
result.add(metadataManager.loadRepository(location, null));
artifactManager.addRepository(location);
} catch (URISyntaxException e) {
LogUtility.logError("Invalid URI for repository " + location, e);
} catch (ProvisionException e) {
LogUtility.logWarning("Unable to load repository " + location, e);
}
}
return (IMetadataRepository[]) result.toArray(new IMetadataRepository[result.size()]);
}
public Collection getInstalled(String id) {
IProfile profile = registry.getProfile(id);
if (profile == null)
return Collections.EMPTY_LIST;
IQuery query = new IUProfilePropertyQuery(PROP_TOAST_ROOT, "true");
IQueryResult result = profile.query(query, new NullProgressMonitor());
return result.toUnmodifiableSet();
}
private IInstallableUnit findFeature(String feature) {
Collection features = getAvailableFeatures();
for (Iterator i = features.iterator(); i.hasNext();) {
IInstallableUnit unit = (IInstallableUnit) i.next();
if (unit.getId().equals(feature))
return unit;
}
return null;
}
public IStatus install(String id, String feature, IProgressMonitor monitor) {
IProfile profile = registry.getProfile(id);
if (profile == null)
return new Status(IStatus.ERROR, LogUtility.getStatusId(this), "Cannot find profile for: " + id);
IInstallableUnit unit = findFeature(feature);
if (unit == null)
return new Status(IStatus.ERROR, LogUtility.getStatusId(this), "Cannot find feature : " + feature);
IProfileChangeRequest request = planner.createChangeRequest(profile);
request.add(unit);
request.setInstallableUnitProfileProperty(unit, PROP_TOAST_ROOT, "true");
return performOperation(profile, request, monitor);
}
public IStatus uninstall(String id, String feature, IProgressMonitor monitor) {
IProfile profile = registry.getProfile(id);
if (profile == null)
return new Status(IStatus.ERROR, LogUtility.getStatusId(this), "Cannot find profile for: " + id);
IInstallableUnit unit = findFeature(feature);
if (unit == null)
return new Status(IStatus.ERROR, LogUtility.getStatusId(this), "Cannot find feature : " + feature);
IProfileChangeRequest request = planner.createChangeRequest(profile);
request.remove(unit);
return performOperation(profile, request, monitor);
}
private IStatus performOperation(IProfile profile, IProfileChangeRequest request, IProgressMonitor monitor) {
ProvisioningContext context = new ProvisioningContext(agent);
IProvisioningPlan plan = planner.getProvisioningPlan(request, context, monitor);
if (!plan.getStatus().isOK())
return plan.getStatus();
// add the id IU if its not already installed.
plan = installIdIU(plan);
return engine.perform(plan, monitor);
}
public void addProfile(String id, Map properties) {
IProfile profile = registry.getProfile(id);
if (profile != null || properties == null)
return;
String location = new File(URIUtil.append(dataLocation, id)).toString();
String environment = "osgi.os=" + properties.get("osgi.os");
environment += ",osgi.ws=" + properties.get("osgi.ws");
environment += ",osgi.arch=" + properties.get("osgi.arch");
Map props = new HashMap();
props.put(IProfile.PROP_INSTALL_FOLDER, location);
props.put(IProfile.PROP_CACHE, location);
props.put(IProfile.PROP_ENVIRONMENTS, environment);
try {
profile = registry.addProfile(id, props);
} catch (ProvisionException e) {
LogUtility.logError("Error adding profile: " + id, e);
}
}
public Collection getProfiles() {
IProfile[] profiles = registry.getProfiles();
Collection result = new ArrayList(profiles.length);
for (int i = 0; i < profiles.length; i++)
result.add(profiles[i].getProfileId());
return result;
}
public void removeProfile(String id) {
registry.removeProfile(id);
}
/**
* Return a collection of features that are available to be installed.
*/
public Collection getAvailableFeatures() {
IQuery query = QueryUtil.createIUPropertyQuery(PROP_TOAST_ROOT, Boolean.TRUE.toString());
IQueryResult result = metadataManager.query(query, new NullProgressMonitor());
return result.toSet();
}
/**
* Return the collection of features that are available to be installed on
* the device with the given id.
*/
public Collection getAvailableFeatures(String id) {
Collection result = getAvailableFeatures();
result.removeAll(getInstalled(id));
return result;
}
/**
* Add in an IU that sets the toast.id system property to identify the
* client running this profile
*/
private IProvisioningPlan installIdIU(IProvisioningPlan plan) {
IQuery query = QueryUtil.createIUQuery("toast.id");
IQueryResult toastIU = plan.getProfile().query(query, null);
// If we find the id IU then it is already installed.
// if (!toastIU.isEmpty())
// return plan;
IInstallableUnit idIU = createIdIU(plan.getProfile().getProfileId());
plan.addInstallableUnit(idIU);
return plan;
}
private IInstallableUnit createIdIU(String id) {
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
String time = Long.toString(System.currentTimeMillis());
iu.setId("toast.id");
iu.setVersion(Version.createOSGi(0, 0, 0, time));
Map touchpointData = new HashMap();
String data = "addJvmArg(jvmArg:-D" + ICoreConstants.ID_PROPERTY + "=" + id + ");";
touchpointData.put("configure", data);
data = "removeJvmArg(jvmArg:-D" + ICoreConstants.ID_PROPERTY + "=" + id + ");";
touchpointData.put("unconfigure", data);
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
ITouchpointType touchpoint = MetadataFactory.createTouchpointType("org.eclipse.equinox.p2.osgi", Version.createOSGi(1, 0, 0));
iu.setTouchpointType(touchpoint);
return MetadataFactory.createInstallableUnit(iu);
}
}