diff options
author | John Arthorne | 2007-09-30 18:11:02 +0000 |
---|---|---|
committer | John Arthorne | 2007-09-30 18:11:02 +0000 |
commit | 2ccb446cc885495686cc9eb430c4a98031b3cfc5 (patch) | |
tree | 207e30d5f61856ece926ed3548297aa3958132c9 /bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox | |
parent | c4f6662ccd02bb75cba95a6ae56a2da38c6cadde (diff) | |
download | rt.equinox.p2-2ccb446cc885495686cc9eb430c4a98031b3cfc5.tar.gz rt.equinox.p2-2ccb446cc885495686cc9eb430c4a98031b3cfc5.tar.xz rt.equinox.p2-2ccb446cc885495686cc9eb430c4a98031b3cfc5.zip |
Renamed prov bundles to p2
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox')
36 files changed, 2328 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/CommonDef.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/CommonDef.java new file mode 100644 index 000000000..e24937a7f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/CommonDef.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2007 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.internal.prov.engine; + +/* + * This interface contains global constant definitions. + * + * Use this interface to define constants that are likely to be used + * widely in different contexts with share a common intended meaning. + * + */ +public interface CommonDef { + + public static final String EmptyString = ""; //$NON-NLS-1$ + public static final String SpaceString = " "; //$NON-NLS-1$ + public static final String Underscore = "_"; //$NON-NLS-1$ + public static final String Dot = "."; //$NON-NLS-1$ + public static final String DotDot = ".."; //$NON-NLS-1$ + + public static final String EncodedSpaceString = "%20"; //$NON-NLS-1$ + + public static final String UncPrefix = "\\\\"; //$NON-NLS-1$ + public static final char ColonChar = ':'; + + /* + * Strings used as the type for the native and eclipse touchpoints, + * including the type in the touchpoints extension point. + */ + public static final String NativeTouchpoint = "native"; //$NON-NLS-1$ + public static final String EclipseTouchpoint = "eclipse"; //$NON-NLS-1$ + + public static final int MaxPathLength_Win32 = 256; + public static final int MaxPathLength_Linux = 1024; + // + // /* + // * Different protocols + // */ + // public interface Protocols { + // public static final String File = "file"; //$NON-NLS-1$ + // public static final String Http = "http"; //$NON-NLS-1$ + // public static final String Https = "https"; //$NON-NLS-1$ + // public static final String Ftp = "ftp"; //$NON-NLS-1$ + // public static final String Socks = "SOCKS"; //$NON-NLS-1$ + // } + // + // /* + // * File name extensions. + // */ + // public interface Extensions { + // public static final String Xml = ".xml"; //$NON-NLS-1$ + // public static final String Zip = ".zip"; //$NON-NLS-1$ + // public static final String Jar = ".jar"; //$NON-NLS-1$ + // public static final String Properties = ".properties"; //$NON-NLS-1$ + // } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/EngineActivator.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/EngineActivator.java new file mode 100644 index 000000000..ee0b3eeac --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/EngineActivator.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2007 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.internal.prov.engine; + +import org.osgi.framework.*; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.eclipse.equinox.prov.core.eventbus.ProvisioningEventBus; +import org.eclipse.equinox.prov.engine.Engine; + +public class EngineActivator implements BundleActivator, ServiceTrackerCustomizer { + private static BundleContext context; + public static final String ID = "org.eclipse.equinox.prov.engine"; + + public static BundleContext getContext() { + return context; + } + + private ServiceRegistration registration; + private ServiceTracker tracker; + + public void start(BundleContext context) throws Exception { + EngineActivator.context = context; + tracker = new ServiceTracker(context, ProvisioningEventBus.class.getName(), this); + tracker.open(); + } + + public void stop(BundleContext context) throws Exception { + tracker.close(); + tracker = null; + + EngineActivator.context = null; + } + + public Object addingService(ServiceReference reference) { + if (registration == null) { + ProvisioningEventBus eventBus = (ProvisioningEventBus) context.getService(reference); + registration = context.registerService(Engine.class.getName(), new Engine(eventBus), null); + return eventBus; + } + return null; + } + + public void modifiedService(ServiceReference reference, Object service) { + // TODO Auto-generated method stub + + } + + public void removedService(ServiceReference reference, Object service) { + if (registration != null) { + registration.unregister(); + registration = null; + } + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/Messages.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/Messages.java new file mode 100644 index 000000000..c1dcd38fe --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/Messages.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007 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.internal.prov.engine; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.prov.engine.messages"; //$NON-NLS-1$ + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } + + public static String Engine_Error_During_Phase; + public static String Engine_Operation_Canceled_By_User; + public static String Profile_Not_Named_Self; + public static String Profile_Duplicate_Profile_Id; + public static String TouchpointManager_Null_Touchpoint_Type_Argument; + public static String TouchpointManager_Required_Touchpoint_Not_Found; + public static String TouchpointManager_No_Extension_Point; + public static String TouchpointManager_Incorrectly_Named_Extension; + public static String TouchpointManager_Attribute_Not_Specified; + public static String TouchpointManager_Conflicting_Touchpoint_Types; + public static String TouchpointManager_Touchpoint_Type_Mismatch; + public static String TouchpointManager_Exception_Creating_Touchpoint_Extension; + public static String TouchpointManager_Null_Creating_Touchpoint_Extension; + public static String Install_Operand_Description; + public static String Update_Operand_Description; + public static String Uninstall_Operand_Description; + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/NullTouchpoint.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/NullTouchpoint.java new file mode 100644 index 000000000..2b8795770 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/NullTouchpoint.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 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.internal.prov.engine; + +import org.eclipse.equinox.prov.engine.*; +import org.eclipse.equinox.prov.metadata.TouchpointType; + +/** + * A touchpoint that performs no processing. + */ +public class NullTouchpoint implements ITouchpoint { + public static final ITouchpoint INSTANCE = new NullTouchpoint(); + + /** + * Public constructor only intended to be called by extension registry. + */ + public NullTouchpoint() { + super(); + } + + public TouchpointType getTouchpointType() { + return TouchpointType.NONE; + } + + public boolean supports(String phaseId) { + if (phaseId.equals("install") || phaseId.equals("uninstall")) + return true; + return false; + } + + public ITouchpointAction[] getActions(String phaseID, Profile profile, Operand operand) { + return new ITouchpointAction[] {}; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/TouchpointManager.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/TouchpointManager.java new file mode 100644 index 000000000..ec2cf09a5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/TouchpointManager.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2007 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.internal.prov.engine; + +import java.util.*; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.prov.core.helpers.LogHelper; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; +import org.eclipse.equinox.prov.engine.ITouchpoint; +import org.eclipse.equinox.prov.metadata.TouchpointType; +import org.eclipse.osgi.util.NLS; + +//TODO This needs to support multiple version of each touchpoint and have a lookup that supports version semantics +public class TouchpointManager implements IRegistryChangeListener { + + private static TouchpointManager instance; + + public static TouchpointManager getInstance() { + if (instance == null) { + instance = new TouchpointManager(); + } + return instance; + } + + private static final String PT_TOUCHPOINTS = "touchpoints"; //$NON-NLS-1$ + private static final String ELEMENT_TOUCHPOINT = "touchpoint"; //$NON-NLS-1$ + private static final String ELEMENT_TOUCHPOINT_DATA = "data"; //$NON-NLS-1$ + private static final String ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$ + private static final String ATTRIBUTE_TYPE = "type"; //$NON-NLS-1$ + + private class TouchpointEntry { + + private IConfigurationElement element; + private boolean createdExtension; + private ITouchpoint touchpoint; + + public TouchpointEntry(IConfigurationElement element) { + super(); + this.element = element; + this.touchpoint = null; + this.createdExtension = false; + } + + public TouchpointEntry(IConfigurationElement element, ITouchpoint touchpoint) { + super(); + this.element = element; + this.touchpoint = touchpoint; + this.createdExtension = (touchpoint != null ? true : false); + } + + public boolean hasTouchpoint() { + return (this.touchpoint != null); + } + + public ITouchpoint getTouchpoint() { + if (!createdExtension) { + String id = element.getAttribute(ATTRIBUTE_TYPE); + try { + ITouchpoint touchpoint = (ITouchpoint) element.createExecutableExtension(ATTRIBUTE_CLASS); + if (touchpoint != null) { + if (!id.equals(touchpoint.getTouchpointType().getId())) { + reportError(NLS.bind(Messages.TouchpointManager_Touchpoint_Type_Mismatch, id, touchpoint.getTouchpointType().getId()), null); + } + this.touchpoint = touchpoint; + } else { + String errorMsg = NLS.bind(Messages.TouchpointManager_Null_Creating_Touchpoint_Extension, id); + throw new CoreException(new Status(IStatus.ERROR, EngineActivator.ID, 1, errorMsg, null)); + } + } catch (CoreException cexcpt) { + LogHelper.log(new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.TouchpointManager_Exception_Creating_Touchpoint_Extension, id), cexcpt)); + } catch (ClassCastException ccexcpt) { + LogHelper.log(new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.TouchpointManager_Exception_Creating_Touchpoint_Extension, id), ccexcpt)); + } + // Mark as created even in error cases, + // so exceptions are not logged multiple times + createdExtension = true; + } + return this.touchpoint; + } + + public String toString() { + StringBuffer result = new StringBuffer(element.toString()); + if (createdExtension) { + String touchpointString = touchpoint != null ? touchpoint.toString() : "not created"; //$NON-NLS-1$ + result.append(" => " + touchpointString); //$NON-NLS-1$ + } + return result.toString(); + } + } + + // TODO: Do we really want to store the touchpoints? The danger is + // that if two installations are performed simultaneously, then... + // TODO: Figure out locking, concurrency requirements for touchpoints. + private Map touchpointEntries; + + private TouchpointManager() { + RegistryFactory.getRegistry().addRegistryChangeListener(this, EngineActivator.ID); + } + + /* + * Return the touchpoint which is registered for the given id, + * or <code>null</code> if none are registered. + */ + public ITouchpoint getTouchpoint(TouchpointType id) { + if (id == null || CommonDef.EmptyString.equals(id.getId())) + throw new IllegalArgumentException(Messages.TouchpointManager_Null_Touchpoint_Type_Argument); + if (touchpointEntries == null) { + initializeTouchpoints(); + } + TouchpointEntry entry = (TouchpointEntry) touchpointEntries.get(id.getId()); + return entry == null ? null : entry.getTouchpoint(); + } + + public ITouchpoint[] getAllTouchpoints() { + if (touchpointEntries == null) { + initializeTouchpoints(); + } + Collection adapters = touchpointEntries.values(); + + ArrayList touchpoints = new ArrayList(adapters.size()); + for (Iterator iter = adapters.iterator(); iter.hasNext();) { + TouchpointEntry entry = (TouchpointEntry) iter.next(); + ITouchpoint touchpoint = entry.getTouchpoint(); + if (touchpoint != null) { + touchpoints.add(touchpoint); + } + } + return (ITouchpoint[]) touchpoints.toArray(new ITouchpoint[touchpoints.size()]); + } + + public ITouchpoint[] getCreatedTouchpoints() { + if (touchpointEntries == null) + return new ITouchpoint[0]; + Collection adapters = touchpointEntries.values(); + + ArrayList touchpoints = new ArrayList(adapters.size()); + for (Iterator iter = adapters.iterator(); iter.hasNext();) { + TouchpointEntry entry = (TouchpointEntry) iter.next(); + if (entry.hasTouchpoint()) { + ITouchpoint touchpoint = entry.getTouchpoint(); + if (touchpoint != null) { + touchpoints.add(touchpoint); + } + } + } + return (ITouchpoint[]) touchpoints.toArray(new ITouchpoint[touchpoints.size()]); + } + + public IStatus validateTouchpoints(String[] requiredTypes) { + MultiStatus status = (touchpointEntries == null ? initializeTouchpoints() : new MultiStatus()); + + for (int i = 0; i < requiredTypes.length; i++) { + TouchpointEntry entry = (TouchpointEntry) touchpointEntries.get(requiredTypes[i]); + if (entry == null) { + reportError(NLS.bind(Messages.TouchpointManager_Required_Touchpoint_Not_Found, requiredTypes[i]), status); + } + } + return status; + } + + /* + * Construct a map of the extensions that implement the touchpoints extension point. + */ + private MultiStatus initializeTouchpoints() { + MultiStatus status = new MultiStatus(); + IExtensionPoint point = RegistryFactory.getRegistry().getExtensionPoint(EngineActivator.ID, PT_TOUCHPOINTS); + if (point == null) { + reportError(NLS.bind(Messages.TouchpointManager_No_Extension_Point, EngineActivator.ID, PT_TOUCHPOINTS), status); + touchpointEntries = new HashMap(0); + return status; + } + + IExtension[] extensions = point.getExtensions(); + touchpointEntries = new HashMap(extensions.length); + for (int i = 0; i < extensions.length; i++) { + IConfigurationElement[] elements = extensions[i].getConfigurationElements(); + for (int j = 0; j < elements.length; j++) { + String elementName = elements[j].getName(); + if (!ELEMENT_TOUCHPOINT.equalsIgnoreCase(elements[j].getName())) { + if (!ELEMENT_TOUCHPOINT_DATA.equals(elementName)) { // TODO: are 'data' elements still needed? + reportError(NLS.bind(Messages.TouchpointManager_Incorrectly_Named_Extension, elements[j].getName(), ELEMENT_TOUCHPOINT), status); + } + continue; + } + String id = elements[j].getAttribute(ATTRIBUTE_TYPE); + if (id == null) { + reportError(NLS.bind(Messages.TouchpointManager_Attribute_Not_Specified, ATTRIBUTE_TYPE), status); + continue; + } + if (touchpointEntries.get(id) == null) { + touchpointEntries.put(id, new TouchpointEntry(elements[j])); + } else { + reportError(NLS.bind(Messages.TouchpointManager_Conflicting_Touchpoint_Types, ATTRIBUTE_TYPE, id), status); + } + } + } + return status; + } + + private static void reportError(String errorMsg, MultiStatus status) { + Status errorStatus = new Status(IStatus.ERROR, EngineActivator.ID, 1, errorMsg, null); + if (status != null) { + status.add(errorStatus); + } + LogHelper.log(errorStatus); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent) + */ + public void registryChanged(IRegistryChangeEvent event) { + // just flush the cache when something changed. It will be recomputed on demand. + touchpointEntries = null; + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/messages.properties b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/messages.properties new file mode 100644 index 000000000..9b10bd1cb --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/prov/engine/messages.properties @@ -0,0 +1,29 @@ +############################################################################### +# Copyright (c) 2007 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 +############################################################################### + +Engine_Error_During_Phase=Error during phase: {0}. +Engine_Operation_Canceled_By_User=Operation canceled by the user. + +Profile_Duplicate_Profile_Id=Adding profile with duplicate id: {0}. +Profile_Not_Named_Self=Can't have a profile named: {0}. +TouchpointManager_Null_Touchpoint_Type_Argument=A null or empty string was passed as the type of a touchpoint. +TouchpointManager_Required_Touchpoint_Not_Found=The required {0} touchpoint is not included in the installation manager configuration. +TouchpointManager_No_Extension_Point=The extension point {0}.{1} was not found in the platform extension registry. +TouchpointManager_Incorrectly_Named_Extension=A touchpoint extension is incorrectly named {0} instead of {1}. +TouchpointManager_Attribute_Not_Specified=The required attribute {0} is not specified for the touchpoints extension point. +TouchpointManager_Conflicting_Touchpoint_Types=Two or more touchpoint extensions of type {0} are defined. +TouchpointManager_Touchpoint_Type_Mismatch=The touchpoint registered for type {0} reports its type as {1}. +TouchpointManager_Exception_Creating_Touchpoint_Extension=An exception was thrown and caught when creating the extension point instance for the {0} touchpoint. +TouchpointManager_Null_Creating_Touchpoint_Extension=A null object was returned when creating the extension point instance for the {0} touchpoint. + +Install_Operand_Description=Installing unit {0}. +Update_Operand_Description=Updating unit {0} with unit {1}. +Uninstall_Operand_Description=Removing unit {0}. diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/AbstractProvisioningTransaction.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/AbstractProvisioningTransaction.java new file mode 100644 index 000000000..2f48238a2 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/AbstractProvisioningTransaction.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.ArrayList; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.prov.engine.EngineActivator; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; +import org.eclipse.equinox.prov.core.helpers.MultiStatusUtil; +import org.eclipse.osgi.util.NLS; + +/** + * An abstract provisioning transaction specifies a simple mechanism + * for atomicity of a sequence of actions, based on the ability to + * revert the consequences of those actions. + * + * TODO: Implementation(s) of transactions should be registered via + * an extension point by classes that extended this class. + * This class should really be abstract. + */ +public abstract class AbstractProvisioningTransaction { + + private ArrayList actions = new ArrayList(); + + private String description; + + private boolean isUndoable; + + private MultiStatus result; + + private IProgressMonitor progressMonitor; + + public AbstractProvisioningTransaction(String description, boolean isUndoable, MultiStatus result, IProgressMonitor monitor) { + this.description = description; + this.isUndoable = isUndoable; + this.result = (result != null ? result : new MultiStatus()); + this.progressMonitor = monitor; + } + + public IStatus performActions(IProvisioningAction[] ops, int[] weights, IProgressMonitor monitor) { + SubMonitor pm = SubMonitor.convert(monitor, weights.length); + IStatus status = Status.OK_STATUS; + try { + for (int i = 0; i < ops.length; i += 1) { + IProvisioningAction action = ops[i]; + IProgressMonitor sub = (i < weights.length ? (IProgressMonitor) pm.newChild(weights[i]) : new NullProgressMonitor()); + status = performAction(action, sub); + if (MultiStatusUtil.isErrorOrCancel(status)) { + break; + } + } + } finally { + pm.done(); + } + return status; + } + + protected IStatus performAction(IProvisioningAction action, IProgressMonitor monitor) { + actions.add(action); + IStatus opStatus = action.perform(this, monitor); + IStatus status = opStatus; + + if (!isUndoable) { + result.add(opStatus); + status = Status.OK_STATUS; + } else if (MultiStatusUtil.isErrorOrCancel(opStatus)) { + int length = actions.size(); + IProvisioningAction lastAction = (IProvisioningAction) actions.get(length - 1); + if (!lastAction.shouldRevertOnError()) { + actions.remove(length - 1); + } + } else if (monitor.isCanceled()) { + // first time we noticed cancellation + opStatus = new Status(IStatus.CANCEL, EngineActivator.ID, 0, ""/*Messages.Engine_Operation_Canceled_By_User*/, null); + } + + if (opStatus.matches(IStatus.ERROR) && result.getMessage().length() == 0) { + result.setMessage(NLS.bind("Errors occurred during the transaction {0}", //$NON-NLS-1$ + description)); + } else if (opStatus.matches(IStatus.CANCEL) && result.getMessage().length() == 0) { + result.setMessage(NLS.bind("The transaction {0} was canceled", //$NON-NLS-1$ + description)); + } + + monitor.done(); + return status; + } + + public void rollback(IProgressMonitor monitor) { + if (!isUndoable) + return; + isUndoable = false; + + // TODO: is it necessary to allow support rollback that does NOT reverse the order + // of the actions? Consider phases. + + SubMonitor pm = SubMonitor.convert(monitor, actions.size()); + for (int i = actions.size() - 1; i >= 0; i--) { + IProvisioningAction action = (IProvisioningAction) actions.get(i); + try { + IStatus status = action.revert(this, pm.newChild(10)); + // log.statusNotOK(status); + } catch (Exception e) { + // log.exception + } + } + } + + public IProgressMonitor getProgressMonitor() { + return progressMonitor; + } + + public boolean isUndoable() { + return isUndoable; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("Transaction: "); //$NON-NLS-1$ + sb.append(description); + sb.append(", ").append(actions.size()).append( //$NON-NLS-1$ + " actions performed"); //$NON-NLS-1$ + if (!isUndoable) { + sb.append(", isUndoable=").append(isUndoable); //$NON-NLS-1$ + } + return sb.toString(); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/BeginOperationEvent.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/BeginOperationEvent.java new file mode 100644 index 000000000..9acffd2cf --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/BeginOperationEvent.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +public class BeginOperationEvent extends TransactionEvent { + + private static final long serialVersionUID = 6389318375739324865L; + + public BeginOperationEvent(Profile profile, PhaseSet phaseSet, Operand[] deltas, Engine engine) { + super(profile, phaseSet, deltas, engine); + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/CommitOperationEvent.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/CommitOperationEvent.java new file mode 100644 index 000000000..e3bdd998a --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/CommitOperationEvent.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +public class CommitOperationEvent extends TransactionEvent { + private static final long serialVersionUID = -523967775426133720L; + + public CommitOperationEvent(Profile profile, PhaseSet phaseSet, Operand[] deltas, Engine engine) { + super(profile, phaseSet, deltas, engine); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/DefaultPhaseSet.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/DefaultPhaseSet.java new file mode 100644 index 000000000..ec52bb252 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/DefaultPhaseSet.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.equinox.prov.engine.phases.*; + +public class DefaultPhaseSet extends PhaseSet { + + public DefaultPhaseSet() { + super(new Phase[] {new Collect(10), new Uninstall(10), new Install(10)}); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Engine.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Engine.java new file mode 100644 index 000000000..4a81eb640 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Engine.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.ArrayList; +import java.util.List; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.prov.core.eventbus.ProvisioningEventBus; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; + +public class Engine { + + private final ProvisioningEventBus eventBus; + private List lockedProfiles = new ArrayList(); + + public Engine(ProvisioningEventBus eventBus) { + this.eventBus = eventBus; + } + + public MultiStatus perform(Profile profile, PhaseSet phaseSet, Operand[] operands, IProgressMonitor monitor) { + + // TODO -- Messages + if (profile == null) + throw new IllegalArgumentException("Profile must not be null."); + + if (phaseSet == null) + throw new IllegalArgumentException("PhaseSet must not be null."); + + if (operands == null) + throw new IllegalArgumentException("Operands must not be null."); + + if (monitor == null) + monitor = new NullProgressMonitor(); + + if (operands.length == 0) + return new MultiStatus(IStatus.OK, null); + + lockProfile(profile); + try { + eventBus.publishEvent(new BeginOperationEvent(profile, phaseSet, operands, this)); + + EngineSession session = new EngineSession(); + MultiStatus result = phaseSet.perform(session, profile, operands, monitor); + if (result.isOK()) { + eventBus.publishEvent(new CommitOperationEvent(profile, phaseSet, operands, this)); + session.commit(); + } + + if (result.isErrorOrCancel()) { + eventBus.publishEvent(new RollbackOperationEvent(profile, phaseSet, operands, this, result)); + session.rollback(); + } + return result; + } finally { + unlockProfile(profile); + } + } + + private synchronized void unlockProfile(Profile profile) { + lockedProfiles.remove(profile); + notify(); + } + + private synchronized void lockProfile(Profile profile) { + while (lockedProfiles.contains(profile)) { + try { + wait(); + } catch (InterruptedException e) { + // TODO We should think about how we want to handle blocked engine requests + Thread.currentThread().interrupt(); + } + } + lockedProfiles.add(profile); + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/EngineSession.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/EngineSession.java new file mode 100644 index 000000000..893e40ec0 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/EngineSession.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.*; + +public class EngineSession { + List actions = new ArrayList(); + + public void record(ITouchpointAction action) { + actions.add(action); + } + + public void commit() { + actions.clear(); + } + + public void rollback() { + for (ListIterator it = actions.listIterator(actions.size()); it.hasPrevious();) { + ITouchpointAction action = (ITouchpointAction) it.previous(); + action.undo(); + } + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IProfileRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IProfileRegistry.java new file mode 100644 index 000000000..504ae8651 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IProfileRegistry.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +/** + * This encapsulates the access to the profile registry. + * It deals with persistence in a transparent way. + */ +public interface IProfileRegistry { + public static final String SELF = "_SELF_"; //$NON-NLS-1$ + + /** + * Return the profile in the registry that has the given id. If it does not exist, + * then return <code>null</code>. + * + * @param id the profile identifier + * @return the profile or <code>null</code> + */ + Profile getProfile(String id); + + /** + * Return an array of profiles known to this registry. If there are none, then + * return an empty array. + * + * @return the array of profiles + */ + Profile[] getProfiles(); + + /** + * Add the given profile to this profile registry. + * + * @param toAdd the profile to add + * @throws IllegalArgumentException if a profile + * with the same id is already present in the registry. + */ + void addProfile(Profile toAdd) throws IllegalArgumentException; + + /** + * Remove the given profile from this profile registry. + * + * @param toRemove the profile to remove + */ + void removeProfile(Profile toRemove); + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IProvisioningAction.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IProvisioningAction.java new file mode 100644 index 000000000..7b976aa69 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IProvisioningAction.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; + +/** + * The provisioning action interface represents a unit of work + * that can be performed during a provisioning activity. + * The granularity of the action can vary from a simple, + * indivisible unit of work to a complex, organized collection + * of steps. + * <p> + * The results of an provisioning activity must be revert-able + * an error or if a cancellation occurs. A provisioning action + * that will return an error or cancel status may choose to revert + * any work performed before returning or may indicate that + * the caller must explicitly revert. + */ +public interface IProvisioningAction { + + public IStatus perform(AbstractProvisioningTransaction transaction, IProgressMonitor monitor); + + public IStatus revert(AbstractProvisioningTransaction transaction, IProgressMonitor monitor); + + public boolean shouldRevertOnError(); + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ITouchpoint.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ITouchpoint.java new file mode 100644 index 000000000..838d2a3de --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ITouchpoint.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.equinox.prov.metadata.TouchpointType; + +/** + * A touchpoint is responsible for executing a given phase for a given + * targeted system (eclipse, native). The order of phases is defined in the {@link PhaseSet}. + */ +public interface ITouchpoint { + + public TouchpointType getTouchpointType(); + + public boolean supports(String phaseId); + + public ITouchpointAction[] getActions(String phaseId, Profile profile, Operand operand); +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ITouchpointAction.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ITouchpointAction.java new file mode 100644 index 000000000..c7a9a78e7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ITouchpointAction.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +public interface ITouchpointAction { + + Object execute(); + + Object undo(); +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IUPhase.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IUPhase.java new file mode 100644 index 000000000..4ac09ee3c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/IUPhase.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; + +// An operation that is applied to a set of IUs. +public abstract class IUPhase extends Phase { + protected int PRE_PERFORM_WORK = 1000; + protected int PERFORM_WORK = 10000; + protected int POST_PERFORM_WORK = 1000; + + protected IUPhase(String phaseId, int weight, String phaseName) { + super(phaseId, weight, phaseName); + } + + protected void perform(MultiStatus status, EngineSession session, Profile profile, Operand[] operands, IProgressMonitor monitor) { //TODO Maybe should we do some kind of adaptable + SubMonitor subMonitor = SubMonitor.convert(monitor, PRE_PERFORM_WORK + PERFORM_WORK + POST_PERFORM_WORK); + prePerform(status, profile, operands, subMonitor.newChild(PRE_PERFORM_WORK)); + if (status.isErrorOrCancel()) + return; + + subMonitor.setWorkRemaining(PERFORM_WORK + POST_PERFORM_WORK); + mainPerform(status, session, profile, operands, subMonitor.newChild(PERFORM_WORK)); + if (status.isErrorOrCancel()) + return; + + subMonitor.setWorkRemaining(POST_PERFORM_WORK); + postPerform(status, profile, operands, subMonitor.newChild(POST_PERFORM_WORK)); + if (status.isErrorOrCancel()) + return; + + subMonitor.done(); + } + + protected void mainPerform(MultiStatus status, EngineSession session, Profile profile, Operand[] operands, SubMonitor subMonitor) { + int operandWork = PERFORM_WORK / operands.length; + for (int i = 0; i < operands.length; ++i) { + if (subMonitor.isCanceled()) + throw new OperationCanceledException(); + Operand currentOperand = operands[i]; + if (!isApplicable(currentOperand)) + continue; + IStatus result = performOperand(session, profile, currentOperand, subMonitor.newChild(operandWork)); + status.add(result); + if (status.isErrorOrCancel()) + return; + } + } + + protected abstract boolean isApplicable(Operand op); + + // ITouchpoint touchpoint = TouchpointManager.getInstance().getTouchpoint(currentOperand.getTouchpointType()); + // if (touchpoint == null) { //TODO Should we throw an exception instead? + // status.add(new Status(IStatus.ERROR, "org.eclipse.equinox.prov.engine", "The touchpoint " + currentOperand.getTouchpointType() + " is not available.")); + // return; + // } + // if (touchpoint.supports(phaseId)) { + // status.add(performIU(touchpoint, currentOperand, subMonitor.newChild(operandWork))); + // } + // if (status.isErrorOrCancel() || sub.isCanceled()) { + // undoPerform(status, ius, i, context); + // return; + // } + + // Error or cancel: undo IUs that were done. + // private void undoPerform(MultiStatus status, InstallableUnitPair[] ius, int currentIU, InstallContext context) { + // if (!status.isErrorOrCancel()) { + // status.setCanceled(); // first time we noticed cancelation + // currentIU += 1; // currentIU was completed so it must be undone + // } + // InstallableUnitPair[] undoIUs = new InstallableUnitPair[currentIU]; + // for (int i = 0; i < currentIU; i += 1) { + // log.debug("Undo {0} phase for {1}", super.phaseName, ius[i]); //$NON-NLS-1$ + // undoIUs[i] = ius[currentIU - (i + 1)].reverse(); + // } + // // 1 unit to undo this phase, 10 for preceding phases + // SplitProgressMonitor pm = new SplitProgressMonitor(getUndoProgressMonitor(), new int[] {1, 10}); + // doPerform(status, /*undoable*/false, undoIUs, context, pm.next()); + // setUndoProgressMonitor(pm.next()); + // } + protected abstract IStatus performOperand(EngineSession session, Profile profile, Operand operand, IProgressMonitor monitor); + + protected void prePerform(MultiStatus status, Profile profile, Operand[] deltas, IProgressMonitor monitor) { + //Nothing to do. + } + + protected void postPerform(MultiStatus status, Profile profile, Operand[] deltas, IProgressMonitor monitor) { + //Nothing to do. + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/InstallableUnitEvent.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/InstallableUnitEvent.java new file mode 100644 index 000000000..6ba45dc09 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/InstallableUnitEvent.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.EventObject; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class InstallableUnitEvent extends EventObject { + private static final long serialVersionUID = 3318712818811459886L; + + private String phaseId; + private boolean prePhase; + + private Profile profile; + private Operand operand; + private ITouchpoint touchpoint; + private IStatus result; + + public InstallableUnitEvent(String phaseId, boolean prePhase, Profile profile, Operand operand, ITouchpoint touchpoint) { + this(phaseId, prePhase, profile, operand, touchpoint, null); + } + + public InstallableUnitEvent(String phaseId, boolean prePhase, Profile profile, Operand operand, ITouchpoint touchpoint, IStatus result) { + super(touchpoint); //TODO not sure if the touchpoint should be the source + this.phaseId = phaseId; + this.prePhase = prePhase; + this.profile = profile; + this.operand = operand; + this.result = result; + } + + public ITouchpoint getTouchpoint() { + return touchpoint; + } + + public Profile getProfile() { + return profile; + } + + public Operand getOperand() { + return operand; + } + + public String getPhase() { + return phaseId; + } + + public boolean isPre() { + return prePhase; + } + + public boolean isPost() { + return !prePhase; + } + + public IStatus getResult() { + return (result != null ? result : Status.OK_STATUS); + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Operand.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Operand.java new file mode 100644 index 000000000..5a4ac821e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Operand.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.equinox.prov.metadata.IResolvedInstallableUnit; + +public class Operand { + private final IResolvedInstallableUnit first; + private final IResolvedInstallableUnit second; + + public Operand(IResolvedInstallableUnit first, IResolvedInstallableUnit second) { + this.first = first; + this.second = second; + } + + public IResolvedInstallableUnit first() { + return first; + } + + public IResolvedInstallableUnit second() { + return second; + } + + public String toString() { + return first + " --> " + second; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Phase.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Phase.java new file mode 100644 index 000000000..19729096f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Phase.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.equinox.internal.prov.engine.Messages; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; +import org.eclipse.osgi.util.NLS; + +public abstract class Phase { + protected final String phaseId; + protected final int weight; + protected final String phaseName; + + protected Phase(String phaseId, int weight, String phaseName) { + if (phaseId == null || phaseId.length() == 0) { + throw new IllegalArgumentException("Phase id must be set."); + } + + if (weight <= 0) { + throw new IllegalArgumentException("Phase weight must be positive."); + } + + if (phaseName == null || phaseName.length() == 0) { + throw new IllegalArgumentException("Phase name must be set."); + } + + this.weight = weight; + this.phaseName = phaseName; + this.phaseId = phaseId; + } + + public String toString() { + return "Phase: " + this.phaseName + " - " + this.weight; //$NON-NLS-1$ //$NON-NLS-2$ + } + + protected MultiStatus perform(EngineSession session, Profile profile, Operand[] deltas, IProgressMonitor monitor) { + MultiStatus status = new MultiStatus(); + // log.start(log.info(Messages2.Engine_Performing_Phase, this.phaseName)); + perform(status, session, profile, deltas, monitor); + // log.stop(); + if (status.matches(IStatus.CANCEL)) { + status.setMessage(Messages.Engine_Operation_Canceled_By_User); + } else if (status.matches(IStatus.ERROR)) { + status.setMessage(NLS.bind(Messages.Engine_Error_During_Phase, this.phaseName)); + } + return status; + } + + protected abstract void perform(MultiStatus status, EngineSession session, Profile profile, Operand[] deltas, IProgressMonitor monitor); + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/PhaseSet.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/PhaseSet.java new file mode 100644 index 000000000..f5d85a2b4 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/PhaseSet.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; + +public abstract class PhaseSet { + private final Phase[] phases; + + public PhaseSet(Phase[] phases) { + if (phases == null) + throw new IllegalArgumentException("Phases must not be null"); + + this.phases = phases; + } + + public MultiStatus perform(EngineSession session, Profile profile, Operand[] deltas, IProgressMonitor monitor) { + MultiStatus result = new MultiStatus(); + int[] weights = getProgressWeights(); + int totalWork = getTotalWork(weights); + SubMonitor pm = SubMonitor.convert(monitor, totalWork); + try { + for (int i = 0; i < phases.length; i++) { + if (pm.isCanceled()) { + result.setCanceled(); + return result; + } + Phase phase = phases[i]; + result.add(phase.perform(session, profile, deltas, pm.newChild(weights[i]))); + if (result.isErrorOrCancel())// || sub.isCanceled()) { + //TODO Need to perform the undo if we don't we use transactions + return result; + } + } finally { + pm.done(); + } + return result; + } + + private int getTotalWork(int[] weights) { + int sum = 0; + for (int i = 0; i < weights.length; i++) + sum += weights[i]; + return sum; + } + + private int[] getProgressWeights() { + int[] weights = new int[phases.length]; + for (int i = 0; i < phases.length; i += 1) { + weights[i] = phases[i].weight; + } + return weights; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Profile.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Profile.java new file mode 100644 index 000000000..1894f375d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Profile.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.*; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.equinox.internal.prov.engine.EngineActivator; +import org.eclipse.equinox.prov.core.helpers.ServiceHelper; +import org.eclipse.equinox.prov.installregistry.IInstallRegistry; +import org.eclipse.equinox.prov.installregistry.IProfileInstallRegistry; +import org.eclipse.equinox.prov.metadata.IInstallableUnit; +import org.eclipse.equinox.prov.metadata.RequiredCapability; +import org.eclipse.equinox.prov.query.IQueryable; +import org.eclipse.equinox.prov.query.QueryableArray; +import org.eclipse.osgi.service.resolver.VersionRange; + +public class Profile implements IQueryable { + + /** + * Profile property constant indicating the flavor for the profile. + */ + public static String PROP_FLAVOR = "eclipse.prov.flavor"; //$NON-NLS-1$ + + /** + * Profile property constant indicating the install folder for the profile. + */ + public static final String PROP_INSTALL_FOLDER = "eclipse.prov.installFolder"; //$NON-NLS-1$ + + /** + * Profile property constant indicating the installed language(s) for the profile. + */ + public static final String PROP_NL = "eclipse.prov.nl"; //$NON-NLS-1$ + + /** + * Profile property constant for a string property indicating a user visible short + * textual description of this profile. May be empty or <code>null</code>, and + * generally will be for non-top level install contexts. + */ + public static final String PROP_DESCRIPTION = "eclipse.prov.description"; //$NON-NLS-1$ + + /** + * Profile property constant for a string property indicating a user visible name of this profile. + * May be empty or <code>null</code>, and generally will be for non-top level + * install contexts. + */ + public static final String PROP_NAME = "eclipse.prov.name"; //$NON-NLS-1$ + + /** + * Profile property constant indicating the list of environments + * (e.g., OS, WS, ...) in which a profile can operate. The value of the property + * is a comma-delimited string of key/value pairs. + */ + public static final String PROP_ENVIRONMENTS = "eclipse.prov.environments"; //$NON-NLS-1$ + + /** + * Profile property constant for a boolean property indicating if the profiling + * is roaming. A roaming profile is one whose physical install location varies + * and is updated whenever it runs. + */ + public static final String PROP_ROAMING = "eclipse.prov.roaming"; //$NON-NLS-1$ + + /** + * Profile property constant indicating the bundle pool cache location. + */ + public static final String PROP_CACHE = "eclipse.prov.cache"; //$NON-NLS-1$ + + //Internal id of the profile + private String profileId; + + private Profile parentProfile; + + /** + * This storage is to be used by the touchpoints to store data. The data must be serializable + */ + private Properties storage = new Properties(); + + public Profile(String profileId) { + if (profileId == null || profileId.length() == 0) { + throw new IllegalArgumentException("Profile id must be set."); + } + this.profileId = profileId; + } + + public String getProfileId() { + return profileId; + } + + public Profile getParentProfile() { + return parentProfile; + } + + public String getValue(String key) { + return storage.getProperty(key); + } + + public void setValue(String key, String value) { + storage.setProperty(key, value); + } + + public Dictionary getSelectionContext() { + Properties result = new Properties(storage); + String environments = storage.getProperty(PROP_ENVIRONMENTS); + if (environments == null) + return result; + for (StringTokenizer tokenizer = new StringTokenizer(environments, ","); tokenizer.hasMoreElements();) { + String entry = tokenizer.nextToken(); + int i = entry.indexOf('='); + String key = entry.substring(0, i).trim(); + String value = entry.substring(i + 1).trim(); + result.put(key, value); + } + return result; + } + + private IInstallableUnit[] getAllInstallableUnits() { + IInstallRegistry registry = (IInstallRegistry) ServiceHelper.getService(EngineActivator.getContext(), IInstallRegistry.class.getName()); + if (registry == null) + return null; + IProfileInstallRegistry profile = registry.getProfileInstallRegistry(new Profile(profileId)); + if (profile == null) + return null; + return profile.getInstallableUnits(); + } + + public Iterator getIterator(String id, VersionRange range, RequiredCapability[] requirements, boolean and) { + return new QueryableArray(getAllInstallableUnits()).getIterator(id, range, requirements, and); + } + + public IInstallableUnit[] query(String id, VersionRange range, RequiredCapability[] requirements, boolean and, IProgressMonitor progress) { + return new QueryableArray(getAllInstallableUnits()).query(id, range, requirements, and, progress); + } + + public Iterator getInstallableUnits() { + return Arrays.asList(getAllInstallableUnits()).iterator(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ProfileEvent.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ProfileEvent.java new file mode 100644 index 000000000..cbb120e67 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ProfileEvent.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.EventObject; + +public class ProfileEvent extends EventObject { + private static final long serialVersionUID = 3082402920617281765L; + + public static byte ADDED = 0; + public static byte REMOVED = 1; + + private byte reason; + + public ProfileEvent(Profile source, byte reason) { + super(source); + this.reason = reason; + } + + public byte getReason() { + return reason; + } + + public Profile getProfile() { + return (Profile) getSource(); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ProvisioningConfigurationException.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ProvisioningConfigurationException.java new file mode 100644 index 000000000..4c2496080 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/ProvisioningConfigurationException.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +public class ProvisioningConfigurationException extends RuntimeException { + + private static final long serialVersionUID = -712627437440533809L; + + public ProvisioningConfigurationException(String name) { + super(name); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/RollbackOperationEvent.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/RollbackOperationEvent.java new file mode 100644 index 000000000..9de0d51c1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/RollbackOperationEvent.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.core.runtime.IStatus; + +public class RollbackOperationEvent extends TransactionEvent { + + private static final long serialVersionUID = -2076492953949691215L; + private IStatus cause; + + public RollbackOperationEvent(Profile profile, PhaseSet phaseSet, Operand[] deltas, Engine engine, IStatus cause) { + super(profile, phaseSet, deltas, engine); + this.cause = cause; + } + + public IStatus getStatus() { + return cause; + } +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/SimpleProfileRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/SimpleProfileRegistry.java new file mode 100644 index 000000000..ae4c4c622 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/SimpleProfileRegistry.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import com.thoughtworks.xstream.XStream; +import java.io.*; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Properties; +import org.eclipse.equinox.internal.prov.engine.EngineActivator; +import org.eclipse.equinox.internal.prov.engine.Messages; +import org.eclipse.equinox.prov.core.eventbus.ProvisioningEventBus; +import org.eclipse.equinox.prov.core.helpers.ServiceHelper; +import org.eclipse.equinox.prov.core.location.AgentLocation; +import org.eclipse.osgi.service.datalocation.Location; +import org.eclipse.osgi.util.NLS; + +public class SimpleProfileRegistry implements IProfileRegistry { + private static String STORAGE = "profileRegistry.xml"; //$NON-NLS-1$ + + /** + * Map of String(Profile id)->Profile. + */ + private LinkedHashMap profiles = new LinkedHashMap(8); + + private Properties properties = new Properties(); + + private String self; + + public SimpleProfileRegistry() { + self = EngineActivator.getContext().getProperty("eclipse.prov.profile"); //$NON-NLS-1$ + restore(); + updateRoamingProfile(); + } + + /** + * If the current profile for self is marked as a roaming profile, we need + * to update its install and bundle pool locations. + */ + private void updateRoamingProfile() { + Profile selfProfile = getProfile(SELF); + if (selfProfile == null) + return; + //only update if self is a roaming profile + if (!Boolean.valueOf(selfProfile.getValue(Profile.PROP_ROAMING)).booleanValue()) + return; + Location installLocation = (Location) ServiceHelper.getService(EngineActivator.getContext(), Location.class.getName(), Location.INSTALL_FILTER); + String locationString = installLocation.getURL().getPath(); + boolean changed = false; + if (!locationString.equals(selfProfile.getValue(Profile.PROP_INSTALL_FOLDER))) { + selfProfile.setValue(Profile.PROP_INSTALL_FOLDER, locationString); + changed = true; + } + if (!locationString.equals(selfProfile.getValue(Profile.PROP_CACHE))) { + selfProfile.setValue(Profile.PROP_CACHE, locationString); + changed = true; + } + if (changed) + persist(); + } + + public String toString() { + return this.profiles.toString(); + } + + public Profile getProfile(String id) { + if (SELF.equals(id)) + id = self; + return (Profile) profiles.get(id); + } + + public Profile[] getProfiles() { + return (Profile[]) profiles.values().toArray(new Profile[profiles.size()]); + } + + public void addProfile(Profile toAdd) throws IllegalArgumentException { + if (isNamedSelf(toAdd)) + throw new IllegalArgumentException(NLS.bind(Messages.Profile_Not_Named_Self, toAdd.getProfileId())); + String id = toAdd.getProfileId(); + if (getProfile(id) == null) { + profiles.put(id, toAdd); + } else + throw new IllegalArgumentException(NLS.bind(Messages.Profile_Duplicate_Profile_Id, id)); + broadcastChangeEvent(toAdd, ProfileEvent.ADDED); + persist(); //TODO This is not enough to keep track of the changes that are being done in a profile. This will likely have to be based on some event like commit + } + + private void broadcastChangeEvent(Profile profile, byte reason) { + ((ProvisioningEventBus) ServiceHelper.getService(EngineActivator.getContext(), ProvisioningEventBus.class.getName())).publishEvent(new ProfileEvent(profile, reason)); + } + + private void restore() { + try { + BufferedInputStream bif = null; + try { + Location agent = (Location) ServiceHelper.getService(EngineActivator.getContext(), AgentLocation.class.getName()); + if (agent == null) + // TODO should likely do something here since we failed to restore. + return; + bif = new BufferedInputStream(new URL(agent.getURL(), STORAGE).openStream()); + XStream xml = new XStream(); + Object[] read = (Object[]) xml.fromXML(bif); + properties = (Properties) read[0]; + profiles = (LinkedHashMap) read[1]; + } finally { + if (bif != null) + bif.close(); + } + } catch (FileNotFoundException e) { + //This is ok. + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void persist() { + OutputStream os; + try { + Location agent = (Location) ServiceHelper.getService(EngineActivator.getContext(), AgentLocation.class.getName()); + if (agent == null) + // TODO should likely do something here since we failed to persist. + return; + if (!agent.getURL().getProtocol().equals("file")) + throw new IOException("can't write at the given location"); + + File outputFile = new File(agent.getURL().getFile(), STORAGE); + if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) + throw new RuntimeException("can't persist profile registry at: " + outputFile); + os = new BufferedOutputStream(new FileOutputStream(outputFile)); + try { + XStream xstream = new XStream(); + xstream.toXML(new Object[] {properties, profiles}, os); + } finally { + os.close(); + } + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + public void removeProfile(Profile toRemove) { + if (isNamedSelf(toRemove)) + throw new IllegalArgumentException(NLS.bind(Messages.Profile_Not_Named_Self, toRemove.getProfileId())); + if (profiles.remove(toRemove.getProfileId()) == null) + return; + broadcastChangeEvent(toRemove, ProfileEvent.REMOVED); + persist(); + } + + private boolean isNamedSelf(Profile p) { + if (SELF.equals(p.getParentProfile())) + return true; + return false; + } + + public Properties getProperties() { + return properties; + } + + public String getProperty(String key) { + return properties.getProperty(key); + } + + public void setProperty(String key, String value) { + properties.setProperty(key, value); + } + + public void removeProperty(String key) { + properties.remove(key); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/TransactionEvent.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/TransactionEvent.java new file mode 100644 index 000000000..9249735a1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/TransactionEvent.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import java.util.EventObject; + +public abstract class TransactionEvent extends EventObject { + protected Profile profile; + protected PhaseSet phaseSet; + protected Operand[] deltas; + + public TransactionEvent(Profile profile, PhaseSet phaseSet, Operand[] deltas, Engine engine) { + super(engine); + this.profile = profile; + this.phaseSet = phaseSet; + this.deltas = deltas; + } + + public Profile getProfile() { + return profile; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Utils.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Utils.java new file mode 100644 index 000000000..949119d6b --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/Utils.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine; + +import org.eclipse.core.runtime.IAdaptable; + +public class Utils { + + //This method will return the adapter, or will throw an exception + public static Object getAdapter(IAdaptable toAdapt, Class toAdaptType) throws ProvisioningConfigurationException { + Object result = toAdapt.getAdapter(toAdaptType); + if (result == null) + throw new ProvisioningConfigurationException("Adaptation failure. Can't adapt :" + toAdapt.getClass().getName() + " into a" + toAdaptType.getName()); + return result; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Collect.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Collect.java new file mode 100644 index 000000000..e0cff6d83 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Collect.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine.phases; + +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.prov.engine.TouchpointManager; +import org.eclipse.equinox.prov.artifact.repository.IArtifactRequest; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; +import org.eclipse.equinox.prov.download.DownloadManager; +import org.eclipse.equinox.prov.engine.*; +import org.eclipse.equinox.prov.metadata.IInstallableUnit; +import org.eclipse.osgi.util.NLS; + +/** + * The goal of the collect phase is to ask the touchpoints if the artifacts associated with an IU need to be downloaded. + */ +public class Collect extends IUPhase { + private static final String PHASE_ID = "collect"; //$NON-NLS-1$ + private DownloadManager dm = null; + + public Collect(int weight) { + super(PHASE_ID, weight, Messages.Engine_Collect_Phase); + //re-balance work since postPerform will do almost all the time-consuming work + PRE_PERFORM_WORK = 0; + PERFORM_WORK = 100; + POST_PERFORM_WORK = 1000; + } + + protected IStatus performOperand(EngineSession session, Profile profile, Operand operand, IProgressMonitor monitor) { + IInstallableUnit unit = operand.second(); + + if (unit != null) { + monitor.subTask(NLS.bind(Messages.Engine_Collecting_For_IU, unit.getId())); + + // TODO: Need do progress reporting + + // Ask all the touchpoints if they need to download an artifact + ITouchpoint touchpoint = TouchpointManager.getInstance().getTouchpoint(unit.getTouchpointType()); + if (touchpoint.supports(PHASE_ID)) { + ITouchpointAction[] actions = touchpoint.getActions(PHASE_ID, profile, operand); + for (int i = 0; i < actions.length; i++) { + Object result = actions[i].execute(); + if (result != null) + dm.add((IArtifactRequest[]) result); + session.record(actions[i]); + } + } + + if (monitor.isCanceled()) + return Status.CANCEL_STATUS; + } + + return Status.OK_STATUS; + } + + protected void postPerform(MultiStatus status, Profile profile, Operand[] deltas, IProgressMonitor monitor) { + // Start the download + status.add(dm.start(monitor)); + } + + protected void prePerform(MultiStatus status, Profile profile, Operand[] deltas, IProgressMonitor monitor) { + dm = new DownloadManager(); + } + + protected boolean isApplicable(Operand op) { + if (op.second() != null) + return true; + return false; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Install.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Install.java new file mode 100644 index 000000000..660d2f74e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Install.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine.phases; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.equinox.internal.prov.engine.EngineActivator; +import org.eclipse.equinox.internal.prov.engine.TouchpointManager; +import org.eclipse.equinox.prov.core.eventbus.ProvisioningEventBus; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; +import org.eclipse.equinox.prov.core.helpers.ServiceHelper; +import org.eclipse.equinox.prov.engine.*; +import org.eclipse.equinox.prov.metadata.IInstallableUnit; +import org.eclipse.osgi.util.NLS; + +public class Install extends IUPhase { + + private static final String PHASE_ID = "install"; //$NON-NLS-1$ + + public Install(int weight) { + super(PHASE_ID, weight, Messages.Engine_Install_Phase); + } + + protected IStatus performOperand(EngineSession session, Profile profile, Operand operand, IProgressMonitor monitor) { + IInstallableUnit unit = operand.second(); + + monitor.subTask(NLS.bind(Messages.Engine_Installing_IU, unit.getId())); + + ITouchpoint touchpoint = TouchpointManager.getInstance().getTouchpoint(unit.getTouchpointType()); + if (!touchpoint.supports(PHASE_ID)) + ((ProvisioningEventBus) ServiceHelper.getService(EngineActivator.getContext(), ProvisioningEventBus.class.getName())).publishEvent(new InstallableUnitEvent(PHASE_ID, true, profile, operand, touchpoint)); + + ITouchpointAction[] actions = touchpoint.getActions(PHASE_ID, profile, operand); + MultiStatus result = new MultiStatus(); + for (int i = 0; i < actions.length; i++) { + IStatus actionStatus = (IStatus) actions[i].execute(); + result.add(actionStatus); + if (!actionStatus.isOK()) + return result; + + session.record(actions[i]); + } + ((ProvisioningEventBus) ServiceHelper.getService(EngineActivator.getContext(), ProvisioningEventBus.class.getName())).publishEvent(new InstallableUnitEvent(PHASE_ID, false, profile, operand, touchpoint, result)); + return result; + } + + protected boolean isApplicable(Operand op) { + if (op.second() != null) + return true; + return false; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Messages.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Messages.java new file mode 100644 index 000000000..f35b810e0 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Messages.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine.phases; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.equinox.prov.engine.phases.messages"; //$NON-NLS-1$ + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } + + public static String Engine_Collect_Phase; + public static String Engine_Collecting_For_IU; + public static String Engine_Install_Phase; + public static String Engine_Installing_IU; + public static String Engine_Uninstall_Phase; + public static String Engine_Uninstalling_IU; +} diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Uninstall.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Uninstall.java new file mode 100644 index 000000000..0732680e1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/Uninstall.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.engine.phases; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.equinox.internal.prov.engine.EngineActivator; +import org.eclipse.equinox.internal.prov.engine.TouchpointManager; +import org.eclipse.equinox.prov.core.eventbus.ProvisioningEventBus; +import org.eclipse.equinox.prov.core.helpers.MultiStatus; +import org.eclipse.equinox.prov.core.helpers.ServiceHelper; +import org.eclipse.equinox.prov.engine.*; +import org.eclipse.equinox.prov.metadata.IInstallableUnit; +import org.eclipse.osgi.util.NLS; + +public class Uninstall extends IUPhase { + + private static final String PHASE_ID = "uninstall"; //$NON-NLS-1$ + + public Uninstall(int weight) { + super(PHASE_ID, weight, Messages.Engine_Uninstall_Phase); + } + + protected IStatus performOperand(EngineSession session, Profile profile, Operand operand, IProgressMonitor monitor) { + IInstallableUnit unit = operand.first(); + + monitor.subTask(NLS.bind(Messages.Engine_Uninstalling_IU, unit.getId())); + + ITouchpoint touchpoint = TouchpointManager.getInstance().getTouchpoint(unit.getTouchpointType()); + if (touchpoint.supports(PHASE_ID)) + ((ProvisioningEventBus) ServiceHelper.getService(EngineActivator.getContext(), ProvisioningEventBus.class.getName())).publishEvent(new InstallableUnitEvent(PHASE_ID, true, profile, operand, touchpoint)); + + //TODO need to protect the actual operation on a try / catch to ensure the delivery of event. + ITouchpointAction[] actions = touchpoint.getActions(PHASE_ID, profile, operand); + MultiStatus result = new MultiStatus(); + for (int i = 0; i < actions.length; i++) { + result.add((IStatus) actions[i].execute()); + session.record(actions[i]); + } + + ((ProvisioningEventBus) ServiceHelper.getService(EngineActivator.getContext(), ProvisioningEventBus.class.getName())).publishEvent(new InstallableUnitEvent(PHASE_ID, false, profile, operand, touchpoint, result)); + return result; + } + + protected boolean isApplicable(Operand op) { + if (op.first() != null) + return true; + return false; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/messages.properties b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/messages.properties new file mode 100644 index 000000000..9e644e32f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/engine/phases/messages.properties @@ -0,0 +1,17 @@ +############################################################################### +# Copyright (c) 2007 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 +############################################################################### + +Engine_Collect_Phase=collect +Engine_Install_Phase=install +Engine_Uninstall_Phase=uninstall +Engine_Collecting_For_IU=Collecting for {0} +Engine_Installing_IU=Installing {0} +Engine_Uninstalling_IU=Uninstalling {0}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/IInstallRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/IInstallRegistry.java new file mode 100644 index 000000000..3cbc8b8f5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/IInstallRegistry.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.installregistry; + +import java.util.Collection; +import org.eclipse.equinox.prov.engine.Profile; + +public interface IInstallRegistry { + + /** + * Save the registry as XML. If exception is thrown, no change was made. + */ + // public void saveXML() throws IOException { + // checkMetadata(); + // File xmlFile = getLocation(); + // plog.start(plog.debug("Saving {0}", xmlFile)); //$NON-NLS-1$ + // if (isEmpty()) { // delete when empty + // xmlFile.delete(); + // if (xmlFile.exists()) { + // throw new IOException(NLS.bind(Messages.InstallRegistry_Failed_To_Delete_Install_Registry, xmlFile)); + // } + // } else { + // new FileUtil.SafeUpdate(xmlFile) { + // public void write(FileOutputStream stream) throws IOException { + // XMLWriter writer = new XMLWriter(stream, XML.getProcessingInstruction()); + // XML.write(writer); + // writer.flush(); + // stream.getFD().sync(); + // writer.close(); + // } + // }.write(); + // } + // plog.stop(); + // } + // private static class XML implements InstallRegistryXML { + // public static XMLWriter.ProcessingInstruction getProcessingInstruction() { + // return new XMLWriter.ProcessingInstruction(PI.INSTALL_REGISTRY, MetaInfo.formatVersion(MetaInfo.INSTALL_REGISTRY_VERSION)); + // } + // + // public static void write(XMLWriter writer) { + // InstallRegistry ir = InstallRegistry.getInstance(); + // writer.start(Elements.INSTALL_REGISTRY); + // writer.write(ir.profileRegistry.getProperties()); + // for (Iterator i = ir.getProfileInstallRegistries().iterator(); i.hasNext();) { + // ProfileInstallRegistry registry = (ProfileInstallRegistry) i.next(); + // if (!registry.isEmpty()) { + // writer.start(Elements.PROFILE); + // Profile profile = registry.getProfile(); + // writer.attribute(Attrs.ID, profile.getProfileId()); + // writer.attribute(Attrs.KIND, profile.getProfileKind()); + // writer.writeProperty(Profile.INSTALL_LOCATION, profile.getInstallLocation()); + // writer.write(profile.getAllData()); + // InstallContext rootContext = profile.getRootContext(); + // if (rootContext != null) { + // write(writer, rootContext); + // } + // registry.emitXML(writer); + // writer.end(Elements.PROFILE); + // } + // } + // writer.end(Elements.INSTALL_REGISTRY); + // } + // + // private static void write(XMLWriter writer, InstallContext installContext) { + // writer.start(Elements.INSTALL_CONTEXT); + // writer.attribute(Attrs.ID, installContext.getId()); + // writer.attribute(Attrs.NAME, installContext.getName()); + // writer.attribute(Attrs.DESCRIPTION, installContext.getDescription()); + // writer.attribute(Attrs.SHAREABLE, installContext.isShareable(), true); + // writer.attribute(Attrs.QUALIFIABLE, installContext.isQualifiable(), false); + // InstallationContextScope scope = installContext.getScope(); + // if (scope != InstallationContextScope.NONE_SCOPE) { + // writer.attribute(Attrs.SCOPE, scope.getName()); + // } + // writer.write(installContext.getLocalProperties()); + // String[] adapterTypes = installContext.getAdaptorTypes(); + // for (int i = 0; i < adapterTypes.length; i += 1) { + // writer.start(Elements.ADAPTER); + // writer.attribute(Attrs.TYPE, adapterTypes[i]); + // writer.end(); + // } + // InstallContext[] subcontexts = installContext.getSubcontexts(); + // for (int i = 0; i < subcontexts.length; i += 1) { + // write(writer, subcontexts[i]); + // } + // writer.end(Elements.INSTALL_CONTEXT); + // } + // } + // /** + // * The file of the install registry. + // */ + // public File getLocation() { + // return this.location; + // } + // // The location of the old install registry directory. + // // This is where the metadata is still stored. + // private File getLegacyLocation(String subdir) { + // String path = getLocation().getPath(); + // if (path.endsWith(CommonDef.Extensions.Xml)) { + // path = path.substring(0, path.length() - CommonDef.Extensions.Xml.length()); + // } else { + // path += ".dir"; //$NON-NLS-1$ + // } + // return new File(path, subdir); + // } + public abstract IProfileInstallRegistry getProfileInstallRegistry(Profile profile); + + /** + * Open the install registry. It must be open before any operations can be performed. + */ + // public void open() throws IOException { + // // openFile(Agent.getInstance().getInstallRegistryLocation()); + // } + // + // // This form is for AbstractAgentTestCase because the preferences aren't set correctly + // // when it needs the install registry. + // public void open(File dir) throws IOException { + // openFile(new File(dir, Agent.FILENAME_INSTALL_REGISTRY)); + // } + // + // private void openFile(File file) throws IOException { + // if (isOpen()) { + // throw new InstallRegistryException(Messages2.InstallRegistry_Install_Registry_Is_Already_Open); + // } + // this.location = file; + // if (this.location.isDirectory()) { + // throw new InstallRegistryException(NLS.bind(Messages2.InstallRegistry_Install_Registry_Exists_And_Is_A_Directory, this.location)); + // } else if (!this.location.exists()) { + // // verify we can write it + // this.location.getParentFile().mkdirs(); + // new FileOutputStream(this.location).close(); + // this.location.delete(); + // } + // + // // TODO: move to cache + // File metadataDir = getLegacyLocation(METADATA_DIR); + // try { + // this.metadataRepo = StandardRepository.create(this.installedMetadata.getRepositoryGroup(), metadataDir); + // } catch (RuntimeException e) { + // // report error below + // } + // if (this.metadataRepo == null) { + // throw new InstallRegistryException(NLS.bind(Messages.InstallRegistry_Failed_To_Create_Install_Registry_Repo, metadataDir)); + // } + // this.metadataRepo.setOpen(true); + // load(); + // checkMetadata(); + // } + // + // public void close() { + // this.installedMetadata.getRepositoryGroup().removeRepository(this.metadataRepo); + // this.metadataRepo = null; + // if (isEmpty()) { + // purge(); + // } + // } + // public void purge() { + // getLocation().delete(); + // FileUtil.rm_r(getLegacyLocation(""), /*removeRoot*/true); //$NON-NLS-1$ + // } + public abstract Collection getProfileInstallRegistries(); + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/IProfileInstallRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/IProfileInstallRegistry.java new file mode 100644 index 000000000..54f7b01fa --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/IProfileInstallRegistry.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.installregistry; + +import org.eclipse.equinox.prov.metadata.IInstallableUnit; + +public interface IProfileInstallRegistry { + + public abstract IInstallableUnit[] getInstallableUnits(); + + public abstract IInstallableUnit getInstallableUnit(String id, String version); + + public abstract void addInstallableUnits(IInstallableUnit toAdd); + + public abstract void removeInstallableUnits(IInstallableUnit toRemove); + + public abstract String getProfileId(); + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/InstallRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/InstallRegistry.java new file mode 100644 index 000000000..2ca377a86 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/prov/installregistry/InstallRegistry.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2007 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.prov.installregistry; + +import com.thoughtworks.xstream.XStream; +import java.io.*; +import java.net.URL; +import java.util.*; +import org.eclipse.equinox.internal.prov.engine.EngineActivator; +import org.eclipse.equinox.prov.core.eventbus.ProvisioningEventBus; +import org.eclipse.equinox.prov.core.eventbus.SynchronousProvisioningListener; +import org.eclipse.equinox.prov.core.helpers.ServiceHelper; +import org.eclipse.equinox.prov.core.location.AgentLocation; +import org.eclipse.equinox.prov.engine.*; +import org.eclipse.equinox.prov.metadata.IInstallableUnit; +import org.eclipse.osgi.service.datalocation.Location; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; + +public class InstallRegistry implements IInstallRegistry { + private static String STORAGE = "installRegistry.xml"; + + // what is installed in each profile + private Map profileRegistries = new HashMap(); // Profile id -> ProfileInstallRegistry + // private ProfileRegistry profileRegistry; // the corresponding ProfileRegistry + // private File location; // xml file containing install registry + // private IRepository metadataRepo; + // private final MetadataCache installedMetadata = new MetadataCache( + // new RepositoryGroup("InstallRegistry"), //$NON-NLS-1$ + // MetadataCache.POLICY_NONE); + + private transient ServiceReference busReference; + private transient ProvisioningEventBus bus; + + public InstallRegistry() { + busReference = EngineActivator.getContext().getServiceReference(ProvisioningEventBus.class.getName()); + bus = (ProvisioningEventBus) EngineActivator.getContext().getService(busReference); + restore(); + bus.addListener(new SynchronousProvisioningListener() { + public void notify(EventObject o) { + if (o instanceof InstallableUnitEvent) { + InstallableUnitEvent event = (InstallableUnitEvent) o; + if (event.isPre() || !event.getResult().isOK()) + return; + IProfileInstallRegistry registry = getProfileInstallRegistry(event.getProfile()); + if (event.getOperand().second() != null) { + registry.addInstallableUnits(event.getOperand().second().getOriginal()); + } + if (event.getOperand().first() != null) { + registry.removeInstallableUnits(event.getOperand().first().getOriginal()); + } + } else if (o instanceof CommitOperationEvent) { + persist(); + return; + } else if (o instanceof RollbackOperationEvent) { + restore(); + return; + } else if (o instanceof ProfileEvent) { + ProfileEvent pe = (ProfileEvent) o; + if (pe.getReason() == ProfileEvent.REMOVED) { + profileRegistries.remove(pe.getProfile().getProfileId()); + persist(); + } + } + } + + }); + } + + private void persist() { + try { + BufferedOutputStream bof = null; + try { + Location agent = (Location) ServiceHelper.getService(EngineActivator.getContext(), AgentLocation.class.getName()); + if (agent == null) + // TODO should likely do something here since we failed to persist. + return; + if (!agent.getURL().getProtocol().equals("file")) + throw new IOException("can't write at the given location"); + + File outputFile = new File(agent.getURL().getFile(), STORAGE); + if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) + throw new RuntimeException("can't persist profile registry"); + bof = new BufferedOutputStream(new FileOutputStream(outputFile, false)); + new XStream().toXML(profileRegistries, bof); + } finally { + if (bof != null) + bof.close(); + } + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void restore() { + try { + BufferedInputStream bif = null; + try { + Location agent = (Location) ServiceHelper.getService(EngineActivator.getContext(), AgentLocation.class.getName()); + if (agent == null) + // TODO should likely do something here since we failed to restore. + return; + bif = new BufferedInputStream(new URL(agent.getURL(), STORAGE).openStream()); + profileRegistries = (HashMap) new XStream().fromXML(bif); + } finally { + if (bif != null) + bif.close(); + } + } catch (FileNotFoundException e) { + //This is ok. + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public IProfileInstallRegistry getProfileInstallRegistry(Profile profile) { + String profileId = profile.getProfileId(); + IProfileInstallRegistry result = (IProfileInstallRegistry) this.profileRegistries.get(profileId); + if (result == null) { + result = new ProfileInstallRegistry(profileId); + this.profileRegistries.put(profileId, result); + } + return result; + } + + public Collection getProfileInstallRegistries() { + return this.profileRegistries.values(); + } + + /** + * Install registry for a single profile. + */ + public class ProfileInstallRegistry implements IProfileInstallRegistry { + private String profileId; // id profile this data applies to + private Set installableUnits; //id + + ProfileInstallRegistry(String profileId) { + this.profileId = profileId; + this.installableUnits = new HashSet(); + } + + public IInstallableUnit[] getInstallableUnits() { + IInstallableUnit[] result = new IInstallableUnit[installableUnits.size()]; + return (IInstallableUnit[]) installableUnits.toArray(result); + } + + public void addInstallableUnits(IInstallableUnit toAdd) { + installableUnits.add(toAdd); + } + + public void removeInstallableUnits(IInstallableUnit toRemove) { + installableUnits.remove(toRemove); + } + + public String getProfileId() { + return profileId; + } + + public IInstallableUnit getInstallableUnit(String id, String version) { + for (Iterator i = installableUnits.iterator(); i.hasNext();) { + IInstallableUnit iu = (IInstallableUnit) i.next(); + if (iu.getId().equals(id) && iu.getVersion().equals(new Version(version))) + return iu; + } + return null; + } + } +} |