bug 397183 - initial support for lastResult variable
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/AssignVariableAction.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/AssignVariableAction.java
new file mode 100644
index 0000000..bcadf35
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/AssignVariableAction.java
@@ -0,0 +1,21 @@
+package org.eclipse.equinox.internal.p2.engine;
+
+import java.util.Map;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
+
+public class AssignVariableAction extends ProvisioningAction {
+
+	@Override
+	public IStatus execute(Map<String, Object> parameters) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public IStatus undo(Map<String, Object> parameters) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ParameterizedProvisioningAction.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ParameterizedProvisioningAction.java
index 8437cb3..eed4c31 100644
--- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ParameterizedProvisioningAction.java
+++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ParameterizedProvisioningAction.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * Copyright (c) 2005, 2012 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
@@ -7,19 +7,21 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Landmark Graphics Corporation - bug 397183
  *******************************************************************************/
 package org.eclipse.equinox.internal.p2.engine;
 
 import java.util.*;
 import java.util.Map.Entry;
 import org.eclipse.core.runtime.IStatus;
-import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
-import org.eclipse.equinox.p2.engine.spi.Touchpoint;
+import org.eclipse.equinox.p2.engine.spi.*;
 
 public class ParameterizedProvisioningAction extends ProvisioningAction {
-
 	private ProvisioningAction action;
 	private Map<String, String> actionParameters;
+	//ActualParameter is used to keep values to which variables have been resolved.
+	//This is especially useful when undoing in the presence of variables that change (e.g. lastResult) 
+	private Map<String, Object> actualParameters;
 	private String actionText;
 
 	public ParameterizedProvisioningAction(ProvisioningAction action, Map<String, String> actionParameters, String actionText) {
@@ -27,6 +29,7 @@
 			throw new IllegalArgumentException(Messages.ParameterizedProvisioningAction_action_or_parameters_null);
 		this.action = action;
 		this.actionParameters = actionParameters;
+		this.actualParameters = new HashMap<String, Object>(actionParameters.size());
 		this.actionText = actionText;
 	}
 
@@ -44,14 +47,14 @@
 		Map<String, Object> result = new HashMap<String, Object>(parameters);
 		for (Entry<String, String> entry : actionParameters.entrySet()) {
 			String name = entry.getKey();
-			String value = processVariables(entry.getValue(), parameters);
+			Object value = processVariables(entry.getValue(), parameters, false);
 			result.put(name, value);
 		}
 		return Collections.unmodifiableMap(result);
 	}
 
-	private String processVariables(String parameterValue, Map<String, Object> parameters) {
-
+	//allowInfixReplacement triggers the replacement of the variables found in the middle of a string (e.g. abc${var}def) 
+	private Object processVariables(String parameterValue, Map<String, Object> parameters, boolean allowInfixReplacement) {
 		int variableBeginIndex = parameterValue.indexOf("${"); //$NON-NLS-1$
 		if (variableBeginIndex == -1)
 			return parameterValue;
@@ -62,7 +65,25 @@
 
 		String preVariable = parameterValue.substring(0, variableBeginIndex);
 		String variableName = parameterValue.substring(variableBeginIndex + 2, variableEndIndex);
-		Object value = parameters.get(variableName);
+
+		//replace the internal name by the user visible name
+		if (Phase.LAST_RESULT_PUBLIC_NAME.equals(variableName)) {
+			variableName = Phase.LAST_RESULT_INTERNAL_NAME;
+		}
+		Object valueUsed = actualParameters.get(variableName);
+		Object value = valueUsed == null ? parameters.get(variableName) : valueUsed;
+		actualParameters.put(variableName, value);
+
+		if (value instanceof Value) {
+			if (allowInfixReplacement == false && variableBeginIndex == 0 && variableEndIndex == parameterValue.length() - 1)
+				return value;
+
+			Value<?> result = (Value<?>) value;
+			if (result.getClazz() == String.class)
+				value = result.getValue();
+			else
+				throw new RuntimeException("The type of the variable is expected to be a String"); //$NON-NLS-1$
+		}
 
 		// try to replace this parameter with a character
 		if (value == null && variableName.charAt(0) == '#') {
@@ -75,8 +96,8 @@
 			}
 		}
 
-		String variableValue = value == null ? "" : value.toString(); //$NON-NLS-1$
-		String postVariable = processVariables(parameterValue.substring(variableEndIndex + 1), parameters);
+		String variableValue = value == null ? "" : value.toString(); //$NON-NLS-1$			//TODO This is where we replace the values
+		String postVariable = (String) processVariables(parameterValue.substring(variableEndIndex + 1), parameters, true);
 		return preVariable + variableValue + postVariable;
 	}
 
@@ -99,4 +120,10 @@
 	public void setTouchpoint(Touchpoint touchpoint) {
 		throw new UnsupportedOperationException();
 	}
+
+	@Override
+	public Value<?> getResult() {
+		return action.getResult();
+	}
+
 }
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Phase.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Phase.java
index bcf9129..ccc05ef 100644
--- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Phase.java
+++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Phase.java
@@ -7,6 +7,7 @@
  * 
  *  Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Landmark Graphics Corporation - bug 397183
  *******************************************************************************/
 package org.eclipse.equinox.internal.p2.engine;
 
@@ -16,8 +17,7 @@
 import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus;
 import org.eclipse.equinox.p2.engine.IProfile;
 import org.eclipse.equinox.p2.engine.ProvisioningContext;
-import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
-import org.eclipse.equinox.p2.engine.spi.Touchpoint;
+import org.eclipse.equinox.p2.engine.spi.*;
 import org.eclipse.osgi.util.NLS;
 
 public abstract class Phase {
@@ -32,6 +32,8 @@
 	protected static final String PARM_AGENT = "agent"; //$NON-NLS-1$
 	protected static final String PARM_FORCED = "forced"; //$NON-NLS-1$
 	protected static final String PARM_TOUCHPOINT = "touchpoint"; //$NON-NLS-1$
+	protected static final String LAST_RESULT_INTERNAL_NAME = "_p2_internal_last_result_variable_"; //$NON-NLS-1$ //
+	protected static final String LAST_RESULT_PUBLIC_NAME = "lastResult"; //$NON-NLS-1$
 
 	protected final String phaseId;
 	protected final int weight;
@@ -155,8 +157,8 @@
 				operandParameters = touchpointToTouchpointOperandParameters.get(operandTouchpoint);
 			}
 
-			operandParameters = Collections.unmodifiableMap(operandParameters);
 			if (actions != null) {
+				Object lastResult = null;
 				for (int j = 0; j < actions.size(); j++) {
 					ProvisioningAction action = actions.get(j);
 					Map<String, Object> parameters = operandParameters;
@@ -168,10 +170,17 @@
 
 						parameters = touchpointToTouchpointOperandParameters.get(touchpoint);
 					}
+					if (lastResult != null && lastResult != Value.NO_VALUE) {
+						parameters = new HashMap<String, Object>(parameters);
+						parameters.put(LAST_RESULT_INTERNAL_NAME, lastResult);
+					}
+					parameters = Collections.unmodifiableMap(parameters);
+
 					IStatus actionStatus = null;
 					try {
 						session.recordActionExecute(action, parameters);
 						actionStatus = action.execute(parameters);
+						lastResult = action.getResult().getValue();
 					} catch (RuntimeException e) {
 						if (!forced)
 							throw e;
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/ProvisioningAction.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/ProvisioningAction.java
index e8d8716..fb80a02 100644
--- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/ProvisioningAction.java
+++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/ProvisioningAction.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2010 IBM Corporation and others.
+ * Copyright (c) 2007, 2012 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Landmark Graphics Corporation - bug 397183
  *******************************************************************************/
 package org.eclipse.equinox.p2.engine.spi;
 
@@ -31,6 +32,16 @@
 
 	public abstract IStatus undo(Map<String, Object> parameters);
 
+	/**
+	 * This method is meant for provisioning actions that need to communicate the result of their execution  
+	 * to subsequent actions.
+	 * This method is only invoked by p2 once execute() has been executed.  
+	 * @return the result of the action execution. 
+	 */
+	public Value<?> getResult() {
+		return Value.NO_VALUE;
+	}
+
 	// TODO: these probably should not be visible
 	public void setTouchpoint(Touchpoint touchpoint) {
 		this.touchpoint = touchpoint;
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/Value.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/Value.java
new file mode 100644
index 0000000..15ecf15
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/spi/Value.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Landmark Graphics Corporation
+ * 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:
+ *     Landmark Graphics Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.engine.spi;
+
+public class Value<T> {
+	public static final Value<Object> NO_VALUE = new Value<Object>(null);
+	private T value;
+	private Class<T> clazz;
+
+	public Value(T val) {
+		value = val;
+	}
+
+	public T getValue() {
+		return value;
+	}
+
+	public Class<T> getClazz() {
+		return clazz;
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.tests/plugin.xml b/bundles/org.eclipse.equinox.p2.tests/plugin.xml
index 021e9ca..cec9954 100644
--- a/bundles/org.eclipse.equinox.p2.tests/plugin.xml
+++ b/bundles/org.eclipse.equinox.p2.tests/plugin.xml
@@ -45,6 +45,14 @@
           version="1.0.0">
     </action>
   </extension>
+   <extension
+       point="org.eclipse.equinox.p2.engine.actions">
+    <action
+          class="org.eclipse.equinox.p2.tests.engine.VariableTest$Action"
+          name="test.actionForVariableTesting"
+          version="1.0.0">
+    </action>
+  </extension>
  <extension
        point="org.eclipse.equinox.p2.engine.actions">
     <action
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/AllTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/AllTests.java
index 69100ff..c420ed9 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/AllTests.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/AllTests.java
@@ -37,6 +37,7 @@
 		suite.addTestSuite(TouchpointManagerTest.class);
 		suite.addTestSuite(TouchpointTest.class);
 		suite.addTestSuite(ProvisioningEventTest.class);
+		suite.addTestSuite(VariableTest.class);
 		return suite;
 	}
 
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/VariableTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/VariableTest.java
new file mode 100644
index 0000000..9df16a5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/VariableTest.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Landmark Graphics Corporation
+ * 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:
+ *     Landmark Graphics Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.tests.engine;
+
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.equinox.p2.engine.*;
+import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
+import org.eclipse.equinox.p2.engine.spi.Value;
+import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
+import org.eclipse.equinox.p2.tests.AbstractProvisioningTest;
+
+public class VariableTest extends AbstractProvisioningTest {
+
+	private IInstallableUnit createIUWithVariable() {
+		InstallableUnitDescription description = new MetadataFactory.InstallableUnitDescription();
+		description.setId("artifactWithZip");
+		description.setVersion(Version.create("1.0.0"));
+		Map touchpointData = new HashMap();
+		touchpointData.put("install", "test.actionForVariableTesting( arg1: expectedValue ); test.actionForVariableTesting ( arg1: ${lastResult} ); test.actionForVariableTesting ( arg1: ${lastResult} )");
+
+		description.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
+
+		return MetadataFactory.createInstallableUnit(description);
+	}
+
+	public void testVariable() {
+		Action.reinitForNextTest();
+		Map properties = new HashMap();
+		properties.put(IProfile.PROP_INSTALL_FOLDER, getTempFolder().getAbsolutePath());
+		IProfile profile = createProfile("testVariable1", properties);
+
+		Iterator ius = getInstallableUnits(profile);
+		assertFalse(ius.hasNext());
+
+		IProvisioningPlan plan = getEngine().createPlan(profile, null);
+		plan.addInstallableUnit(createIUWithVariable());
+		IStatus result = getEngine().perform(plan, PhaseSetFactory.createPhaseSetIncluding(new String[] {PhaseSetFactory.PHASE_INSTALL}), new NullProgressMonitor());
+		assertOK(result);
+		assertEquals("expectedValueaaa", Action.result);
+	}
+
+	public void testUndo() {
+		Action.reinitForNextTest();
+		Action.failMode = true;
+		Map properties = new HashMap();
+		properties.put(IProfile.PROP_INSTALL_FOLDER, getTempFolder().getAbsolutePath());
+		IProfile profile = createProfile("testVariable2", properties);
+
+		Iterator ius = getInstallableUnits(profile);
+		assertFalse(ius.hasNext());
+
+		IProvisioningPlan plan = getEngine().createPlan(profile, null);
+		plan.addInstallableUnit(createIUWithVariable());
+		IStatus result = getEngine().perform(plan, PhaseSetFactory.createPhaseSetIncluding(new String[] {PhaseSetFactory.PHASE_INSTALL}), new NullProgressMonitor());
+		assertNotOK(result);
+		assertEquals(3, Action.inputValues.size());
+		assertEquals(3, Action.undoValues.size());
+
+		//The undo values should be the same than the input values
+		assertEquals(Action.inputValues.get(0), Action.undoValues.get(2));
+		assertEquals(Action.inputValues.get(1), Action.undoValues.get(1));
+		assertEquals(Action.inputValues.get(2), Action.undoValues.get(0));
+	}
+
+	public static class Action extends ProvisioningAction {
+		public static String result;
+		public static boolean failMode;
+		private static int count = 0;
+		private static final int failAt = 3;
+
+		public static ArrayList<String> inputValues = new ArrayList<String>();
+		public static ArrayList<String> undoValues = new ArrayList<String>();
+
+		public static void reinitForNextTest() {
+			inputValues = new ArrayList<String>();
+			undoValues = new ArrayList<String>();
+			failMode = false;
+			count = 0;
+		}
+
+		@Override
+		public IStatus execute(Map<String, Object> parameters) {
+			inputValues.add((String) parameters.get("arg1"));
+			System.out.println(this.hashCode());
+			System.out.println((String) parameters.get("arg1"));
+			count++;
+			if (failMode && count == failAt)
+				throw new RuntimeException("GENERATED Exception");
+			result = ((String) parameters.get("arg1")) + "a";
+
+			return Status.OK_STATUS;
+		}
+
+		@Override
+		public IStatus undo(Map<String, Object> parameters) {
+			undoValues.add((String) parameters.get("arg1"));
+			System.out.println("undo arg --> " + (String) parameters.get("arg1"));
+			return Status.OK_STATUS;
+		}
+
+		public Value<String> getResult() {
+			return new Value<String>(result);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/Messages.java b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/Messages.java
index 0d73c2a..c8aade1 100644
--- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/Messages.java
+++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/Messages.java
@@ -47,6 +47,7 @@
 	public static String download_cache_not_writeable;
 	public static String unzipping;
 	public static String param_not_set;
+	public static String chmod_param_cant_be_set_together;
 	public static String copy_failed;
 	public static String failed_backup_restore;
 	public static String backup_file_failed;
diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ActionConstants.java b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ActionConstants.java
index daa448b..bb22ee5 100644
--- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ActionConstants.java
+++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ActionConstants.java
@@ -33,4 +33,5 @@
 	public static final String PARM_COPY_SOURCE = "source"; //$NON-NLS-1$
 	public static final String PARM_COPY_OVERWRITE = "overwrite"; //$NON-NLS-1$
 	public static final String PARM_OPTIONS = "options"; //$NON-NLS-1$
+	public static final String PARM_ABSOLUTE_FILES = "absoluteFiles"; //$NON-NLS-1$
 }
diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ChmodAction.java b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ChmodAction.java
index 321258d..535e095 100644
--- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ChmodAction.java
+++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/ChmodAction.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- *  Copyright (c) 2008, 2010 IBM Corporation and others.
+ *  Copyright (c) 2008, 2012 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
@@ -9,6 +9,7 @@
  *     Red Hat Incorporated - initial API and implementation
  *     IBM Corporation - ongoing development
  *     Cloudsmith Inc - ongoing development
+ *     Landmark Graphics Corporation - bug 397183
  *******************************************************************************/
 package org.eclipse.equinox.internal.p2.touchpoint.natives.actions;
 
@@ -25,23 +26,52 @@
 	private static final String ACTION_CHMOD = "chmod"; //$NON-NLS-1$
 	private static final boolean WINDOWS = java.io.File.separatorChar == '\\';
 
+	//	target   targetFile   absoluteFiles
+	//	 Y          Y		Y		can't set all these arguments
+	//	 Y          Y		N		Y -> today, add the ability to specifty a list of fileNames
+	//	 Y          N		Y		warning? targetFolder unecessary
+	//	 Y          N		N		incorrect, missing targetFile
+	//	 N          Y		Y		incorrect, target file can't be with absoluteFiles
+	//	 N          Y		N		incorrect, missing the targetFolder
+	//	 N          N		Y               Y
+	//	 N          N		N
 	public IStatus execute(Map<String, Object> parameters) {
+		Object absoluteFiles = parameters.get(ActionConstants.PARM_ABSOLUTE_FILES); //String or String[] 
 		String targetDir = (String) parameters.get(ActionConstants.PARM_TARGET_DIR);
-		if (targetDir == null)
-			return Util.createError(NLS.bind(Messages.param_not_set, ActionConstants.PARM_TARGET_DIR, ACTION_CHMOD));
 		String targetFile = (String) parameters.get(ActionConstants.PARM_TARGET_FILE);
-		if (targetFile == null)
+
+		if (targetFile != null && absoluteFiles != null)
+			return Util.createError(Messages.chmod_param_cant_be_set_together);
+
+		if (targetDir != null && targetFile == null)
 			return Util.createError(NLS.bind(Messages.param_not_set, ActionConstants.PARM_TARGET_FILE, ACTION_CHMOD));
+
+		if (targetDir == null && targetFile != null)
+			return Util.createError(NLS.bind(Messages.param_not_set, ActionConstants.PARM_TARGET_DIR, ACTION_CHMOD));
+
 		String permissions = (String) parameters.get(ActionConstants.PARM_PERMISSIONS);
 		if (permissions == null)
 			return Util.createError(NLS.bind(Messages.param_not_set, ActionConstants.PARM_PERMISSIONS, ACTION_CHMOD));
 		String optionsString = (String) parameters.get(ActionConstants.PARM_OPTIONS);
 
-		// Check that file exist
-		File probe = new File(targetDir + IPath.SEPARATOR + targetFile);
-		if (!probe.exists())
-			return Util.createError(NLS.bind(Messages.action_0_failed_file_1_doesNotExist, ACTION_CHMOD, probe.toString()));
+		String[] filesToProcess = absoluteFiles != null ? ((absoluteFiles instanceof String) ? new String[] {(String) absoluteFiles} : (String[]) absoluteFiles) : makeFilesAbsolute(targetDir, targetFile);
+		for (String fileToChmod : filesToProcess) {
+			// Check that file exist
+			File probe = new File(fileToChmod);
+			if (!probe.exists())
+				return Util.createError(NLS.bind(Messages.action_0_failed_file_1_doesNotExist, ACTION_CHMOD, probe.toString()));
 
+			doChmod(fileToChmod, permissions, optionsString);
+		}
+
+		return Status.OK_STATUS;
+	}
+
+	private String[] makeFilesAbsolute(String targetDir, String targetFile) {
+		return new String[] {new String(targetDir + IPath.SEPARATOR + targetFile)};
+	}
+
+	private void doChmod(String fileToChmod, String permissions, String optionsString) {
 		String options[] = null;
 		if (optionsString != null) {
 			ArrayList<String> collect = new ArrayList<String>();
@@ -63,8 +93,7 @@
 			}
 		}
 
-		chmod(targetDir, targetFile, permissions, options);
-		return Status.OK_STATUS;
+		chmod(fileToChmod, permissions, options);
 	}
 
 	public IStatus undo(Map<String, Object> parameters) {
@@ -72,7 +101,7 @@
 		return Status.OK_STATUS;
 	}
 
-	public void chmod(String targetDir, String targetFile, String perms, String[] options) {
+	public void chmod(String fileToChmod, String perms, String[] options) {
 		if (WINDOWS)
 			return;
 		Runtime r = Runtime.getRuntime();
@@ -86,7 +115,7 @@
 					args[i++] = options[j];
 			}
 			args[i++] = perms;
-			args[i] = targetDir + IPath.SEPARATOR + targetFile;
+			args[i] = fileToChmod;
 			Process process = r.exec(args);
 			readOffStream(process.getErrorStream());
 			readOffStream(process.getInputStream());
diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/CleanupzipAction.java b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/CleanupzipAction.java
index 61a2acf..4c14147 100644
--- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/CleanupzipAction.java
+++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/CleanupzipAction.java
@@ -31,7 +31,7 @@
 	}
 
 	public IStatus undo(Map<String, Object> parameters) {
-		return UnzipAction.unzip(parameters, false);
+		return new UnzipAction().unzip(parameters, false);
 	}
 
 	public static IStatus cleanupzip(Map<String, Object> parameters, boolean restoreable) {
diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/UnzipAction.java b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/UnzipAction.java
index 0d87a57..2c93bba 100644
--- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/UnzipAction.java
+++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/actions/UnzipAction.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2008, 2010 IBM Corporation and others.
+ * Copyright (c) 2008, 2012 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Landmark Graphics Corporation - bug 397183
  *******************************************************************************/
 package org.eclipse.equinox.internal.p2.touchpoint.natives.actions;
 
@@ -17,6 +18,7 @@
 import org.eclipse.equinox.internal.p2.engine.Profile;
 import org.eclipse.equinox.internal.p2.touchpoint.natives.*;
 import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
+import org.eclipse.equinox.p2.engine.spi.Value;
 import org.eclipse.equinox.p2.metadata.IArtifactKey;
 import org.eclipse.equinox.p2.metadata.IInstallableUnit;
 import org.eclipse.osgi.util.NLS;
@@ -24,6 +26,7 @@
 public class UnzipAction extends ProvisioningAction {
 
 	public static final String ACTION_UNZIP = "unzip"; //$NON-NLS-1$
+	private Value<String[]> result;
 
 	public IStatus execute(Map<String, Object> parameters) {
 		return unzip(parameters, true);
@@ -40,7 +43,7 @@
 	 * @param restoreable - if the unzip should be backed up
 	 * @return status
 	 */
-	public static IStatus unzip(Map<String, Object> parameters, boolean restoreable) {
+	public IStatus unzip(Map<String, Object> parameters, boolean restoreable) {
 		String source = (String) parameters.get(ActionConstants.PARM_SOURCE);
 		if (source == null)
 			return Util.createError(NLS.bind(Messages.param_not_set, ActionConstants.PARM_SOURCE, ACTION_UNZIP));
@@ -68,6 +71,12 @@
 		String excludePattern = (String) parameters.get(ActionConstants.PARM_EXCLUDE);
 
 		File[] unzippedFiles = unzip(source, target, path, includePattern, excludePattern, store);
+		String[] filesAsString = new String[unzippedFiles.length];
+		for (int i = 0; i < unzippedFiles.length; i++) {
+			filesAsString[i] = unzippedFiles[i].getAbsolutePath();
+		}
+		result = new Value<String[]>(filesAsString);
+
 		StringBuffer unzippedFileNameBuffer = new StringBuffer();
 		for (int i = 0; i < unzippedFiles.length; i++)
 			unzippedFileNameBuffer.append(unzippedFiles[i].getAbsolutePath()).append(ActionConstants.PIPE);
@@ -96,4 +105,9 @@
 		}
 		return new File[0];
 	}
+
+	@Override
+	public Value<String[]> getResult() {
+		return result;
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties
index 1dbe1a1..1bbd401 100644
--- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties
+++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties
@@ -33,6 +33,7 @@
 artifact_not_available=The artifact for {0} is not available.
 unzipping=Extracting {0}
 param_not_set=The \"{0}\" parameter was not set in the \"{1}\" action.
+chmod_param_cant_be_set_together=The parameters targetFile and absoluteFiles can't be set at the same time.
 artifact_repo_not_found=The artifact repository manager could not be found.
 could_not_obtain_download_cache=Could not obtain the download cache location.
 download_cache_not_writeable=Agent download cache not writeable: {0}.