blob: bc2644951f9cf18aa3b234140dc6dd58fd385b35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 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.equinox.p2.operations;
import java.io.IOException;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.*;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.p2.engine.PhaseSet;
import org.eclipse.equinox.internal.p2.operations.*;
import org.eclipse.equinox.internal.p2.operations.Messages;
import org.eclipse.equinox.internal.provisional.configurator.Configurator;
import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus;
import org.eclipse.equinox.internal.provisional.p2.director.IPlanner;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.Collector;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.InstallableUnitQuery;
import org.eclipse.equinox.p2.core.IAgentLocation;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.engine.*;
import org.eclipse.equinox.p2.engine.query.UserVisibleRootQuery;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.query.IQuery;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
/**
* ProvisioningSession provides the context for a provisioning session, including
* the provisioning services that should be used. It also provides utility
* methods for commonly performed provisioning tasks.
*
* @since 2.0
* @noextend This class is not intended to be subclassed by clients.
*/
public class ProvisioningSession {
private IProvisioningAgent agent;
private Set scheduledJobs = Collections.synchronizedSet(new HashSet());
/**
* A constant indicating that there was nothing to size (there
* was no valid plan that could be used to compute
* size).
*/
public static final long SIZE_NOTAPPLICABLE = -3L;
/**
* Indicates that the size is unavailable (an
* attempt was made to compute size but it failed)
*/
public static final long SIZE_UNAVAILABLE = -2L;
/**
* Indicates that the size is currently unknown
*/
public static final long SIZE_UNKNOWN = -1L;
/**
* A status code used to indicate that there were no updates found when
* looking for updates.
*/
public static final int STATUS_NOTHING_TO_UPDATE = IStatusCodes.NOTHING_TO_UPDATE;
/**
* A status code used to indicate that a repository location was not valid.
*/
public static final int STATUS_INVALID_REPOSITORY_LOCATION = IStatusCodes.INVALID_REPOSITORY_LOCATION;
/**
* Create a provisioning session using the services of the supplied agent.
* @param agent the provisioning agent that supplies services. Must not be <code>null</code>.
*/
public ProvisioningSession(IProvisioningAgent agent) {
Assert.isNotNull(agent, Messages.ProvisioningSession_AgentNotFound);
this.agent = agent;
}
/**
* Return the provisioning agent used to retrieve provisioning services.
* @return the provisioning agent
*/
public IProvisioningAgent getProvisioningAgent() {
return agent;
}
/**
* Return the agent location for this session
* @return the agent location
*/
public IAgentLocation getAgentLocation() {
return (IAgentLocation) agent.getService(IAgentLocation.SERVICE_NAME);
}
/**
* Return the artifact repository manager for this session
* @return the repository manager
*/
public IArtifactRepositoryManager getArtifactRepositoryManager() {
return (IArtifactRepositoryManager) agent.getService(IArtifactRepositoryManager.SERVICE_NAME);
}
/**
* Return the metadata repository manager for this session
* @return the repository manager
*/
public IMetadataRepositoryManager getMetadataRepositoryManager() {
return (IMetadataRepositoryManager) agent.getService(IMetadataRepositoryManager.SERVICE_NAME);
}
/**
* Return the profile registry for this session
* @return the profile registry
*/
public IProfileRegistry getProfileRegistry() {
return (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
}
/**
* Return the provisioning engine for this session
* @return the provisioning engine
*/
public IEngine getEngine() {
return (IEngine) agent.getService(IEngine.SERVICE_NAME);
}
/**
* Return the provisioning event bus used for dispatching events.
* @return the event bus
*/
public IProvisioningEventBus getProvisioningEventBus() {
return (IProvisioningEventBus) agent.getService(IProvisioningEventBus.SERVICE_NAME);
}
/**
* Return the planner used for this session
* @return the planner
*/
public IPlanner getPlanner() {
return (IPlanner) agent.getService(IPlanner.SERVICE_NAME);
}
/**
* Get sizing information about the specified plan.
*
* @param plan the provisioning plan
* @param context the provisioning context to be used for the sizing
* @param monitor the progress monitor
*
* @return a long integer describing the disk size required for the provisioning plan.
*
* @see #SIZE_UNKNOWN
* @see #SIZE_UNAVAILABLE
* @see #SIZE_NOTAPPLICABLE
*/
public long getSize(IProvisioningPlan plan, ProvisioningContext context, IProgressMonitor monitor) {
// If there is nothing to size, return 0
if (plan == null)
return SIZE_NOTAPPLICABLE;
if (plan.getOperands().length == 0)
return 0;
long installPlanSize = 0;
SubMonitor mon = SubMonitor.convert(monitor, 300);
if (plan.getInstallerPlan() != null) {
SizingPhaseSet set = new SizingPhaseSet();
IStatus status = getEngine().perform(plan.getInstallerPlan(), set, mon.newChild(100));
if (status.isOK())
installPlanSize = set.getSizing().getDiskSize();
} else {
mon.worked(100);
}
SizingPhaseSet set = new SizingPhaseSet();
IStatus status = getEngine().perform(plan, set, mon.newChild(200));
if (status.isOK())
return installPlanSize + set.getSizing().getDiskSize();
return SIZE_UNAVAILABLE;
}
/**
* Perform the specified provisioning plan.
*
* @param plan the provisioning plan to be performed
* @param phaseSet the phase set to be used for the plan
* @param context the provisioning context to be used during provisioning
* @param monitor the progress monitor to use while performing the plan
* @return a status describing the result of performing the plan
*/
public IStatus performProvisioningPlan(IProvisioningPlan plan, PhaseSet phaseSet, ProvisioningContext context, IProgressMonitor monitor) {
PhaseSet set;
if (phaseSet == null)
set = new DefaultPhaseSet();
else
set = phaseSet;
// 300 ticks for download, 100 to install handlers, 100 to install the rest
SubMonitor mon = SubMonitor.convert(monitor, 500);
int ticksUsed = 0;
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=272355
// The exact profile instance used in the profile change request and passed to the engine must be used for all
// of these operations, otherwise we can get profile out of synch errors.
IProfile profile = plan.getProfile();
if (plan.getInstallerPlan() != null) {
if (set instanceof DefaultPhaseSet) {
// If the phase set calls for download and install, then we want to download everything atomically before
// applying the install plan. This way, we can be sure to install the install handler only if we know
// we will be able to get everything else.
List allOperands = new ArrayList();
allOperands.addAll(Arrays.asList(plan.getOperands()));
allOperands.addAll(Arrays.asList(plan.getInstallerPlan().getOperands()));
Operand[] downloadOperands = (Operand[]) allOperands.toArray(new Operand[allOperands.size()]);
PhaseSet download = new DownloadPhaseSet();
IProvisioningPlan downloadPlan = getEngine().createCustomPlan(profile, downloadOperands, context);
IStatus downloadStatus = getEngine().perform(downloadPlan, download, mon.newChild(300));
if (!downloadStatus.isOK()) {
mon.done();
return downloadStatus;
}
ticksUsed = 300;
}
// we pre-downloaded if necessary. Now perform the plan against the original phase set.
IStatus installerPlanStatus = getEngine().perform(plan.getInstallerPlan(), set, mon.newChild(100));
if (!installerPlanStatus.isOK()) {
mon.done();
return installerPlanStatus;
}
ticksUsed += 100;
// Apply the configuration
Configurator configChanger = (Configurator) ServiceHelper.getService(Activator.getContext(), Configurator.class.getName());
try {
configChanger.applyConfiguration();
} catch (IOException e) {
mon.done();
return new Status(IStatus.ERROR, Activator.ID, Messages.ProvisioningSession_InstallPlanConfigurationError, e);
}
}
return getEngine().perform(plan, set, mon.newChild(500 - ticksUsed));
}
/**
* Return a boolean indicating whether any other provisioning operations are
* scheduled for the specified profile.
*
* @param profileId the id of the profile in question
* @return <code>true</code> if there are pending provisioning operations for
* this profile, <code>false</code> if there are not.
* @see #rememberJob(Job)
*/
public boolean hasScheduledOperationsFor(String profileId) {
Job[] jobs = getScheduledJobs();
for (int i = 0; i < jobs.length; i++) {
if (jobs[i] instanceof IProfileChangeJob) {
String id = ((IProfileChangeJob) jobs[i]).getProfileId();
if (profileId.equals(id))
return true;
}
}
return false;
}
private Job[] getScheduledJobs() {
synchronized (scheduledJobs) {
return (Job[]) scheduledJobs.toArray(new Job[scheduledJobs.size()]);
}
}
/**
* Remember the specified job. Remembered jobs are
* checked when callers want to know what work is scheduled for
* a particular profile.
*
* @param job the job to be remembered
* @see #hasScheduledOperationsFor(String)
*/
public void rememberJob(Job job) {
scheduledJobs.add(job);
job.addJobChangeListener(new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
scheduledJobs.remove(event.getJob());
}
});
}
/**
* Get the IInstallable units for the specified profile
*
* @param profileId the profile in question
* @param all <code>true</code> if all IInstallableUnits in the profile should
* be returned, <code>false</code> only those IInstallableUnits marked as (user visible) roots
* should be returned.
*
* @return an array of IInstallableUnits installed in the profile.
*/
public IInstallableUnit[] getInstalledIUs(String profileId, boolean all) {
IProfile profile = getProfileRegistry().getProfile(profileId);
if (profile == null)
return new IInstallableUnit[0];
IQuery query;
if (all)
query = InstallableUnitQuery.ANY;
else
query = new UserVisibleRootQuery();
Collector collector = profile.query(query, null);
return (IInstallableUnit[]) collector.toArray(IInstallableUnit.class);
}
}