From c53f7ff6a3eeefcd2de2fa5ab7a12a321f3bfe38 Mon Sep 17 00:00:00 2001
From: Nikolaus Huber <nikolaus.huber@uni-wuerzburg.de>
Date: Wed, 19 Jun 2013 15:26:14 +0000
Subject: [PATCH] + functionality to revert model in case of performance
 degradation

git-svn-id: https://se1.informatik.uni-wuerzburg.de/usvn/svn/code/code/DMM/trunk@12164 9e42b895-fcda-4063-8a3b-11be15eb1bbd
---
 edu.kit.ipd.descartes.adaptation/.classpath   |   1 +
 .../META-INF/MANIFEST.MF                      |  10 +-
 .../adaptation/AdaptationControl.java         |  55 +++---
 .../descartes/adaptation/TacticExecutor.java  |  36 +++-
 .../adaptation/evaluation/IEvaluator.java     |  10 +-
 .../WeightingFunctionEvaluator.java           |   4 +-
 ...rResourceEfficiencyWeightingFunction.java} |   6 +-
 .../dmm/AdaptationProcessModelLoader.java     |  23 ++-
 .../model/dmm/DmmModelActionHandler.java      |  42 +++--
 .../model/dmm/util/DmmModelActionHelper.java  | 168 +++++++++++-------
 .../ObservationRepositoryHandler.java         |   2 +-
 .../util/BlueYonderExperimentTracker.java     |  53 +++++-
 .../descartes/adaptation/util/IObserver.java  |   7 +
 13 files changed, 280 insertions(+), 137 deletions(-)
 rename edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/{ResourceEfficiency.java => BlueYonderResourceEfficiencyWeightingFunction.java} (91%)
 create mode 100644 edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/IObserver.java

diff --git a/edu.kit.ipd.descartes.adaptation/.classpath b/edu.kit.ipd.descartes.adaptation/.classpath
index ad32c83a..4ca2b4b8 100644
--- a/edu.kit.ipd.descartes.adaptation/.classpath
+++ b/edu.kit.ipd.descartes.adaptation/.classpath
@@ -2,6 +2,7 @@
 <classpath>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/edu.kit.ipd.descartes.adaptation/META-INF/MANIFEST.MF b/edu.kit.ipd.descartes.adaptation/META-INF/MANIFEST.MF
index e8ee7016..1e3889ad 100644
--- a/edu.kit.ipd.descartes.adaptation/META-INF/MANIFEST.MF
+++ b/edu.kit.ipd.descartes.adaptation/META-INF/MANIFEST.MF
@@ -4,20 +4,20 @@ Bundle-Name: Adaptation
 Bundle-SymbolicName: edu.kit.ipd.descartes.adaptation;singleton:=true
 Bundle-Version: 1.0.0.qualifier
 Bundle-Activator: edu.kit.ipd.descartes.adaptation.Activator
-Require-Bundle: org.eclipse.osgi;bundle-version="3.8.2",
- org.eclipse.ui,
+Require-Bundle: org.eclipse.ui,
  org.eclipse.core.runtime,
  org.eclipse.ocl,
  edu.kit.ipd.descartes.mm.resourcelandscape;bundle-version="1.0.0",
  edu.kit.ipd.descartes.mm.adaptation;bundle-version="1.0.0",
- edu.kit.ipd.descartes.ql.core.engine;bundle-version="1.0.0"
+ edu.kit.ipd.descartes.ql.core.engine;bundle-version="1.0.0",
+ org.eclipse.emf.ecore.change;bundle-version="2.8.0"
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.6
-Import-Package: edu.kit.ipd.descartes.ql.lang.descartesQL,
+Import-Package: edu.kit.ipd.descartes.ql.core.engine,
+ edu.kit.ipd.descartes.ql.lang.descartesQL,
  edu.kit.ipd.descartes.ql.models.mapping.domain,
  edu.kit.ipd.descartes.ql.models.mapping.mapping,
  edu.kit.ipd.descartes.ql.models.repository,
- org.apache.log4j;version="1.2.15",
  org.eclipse.core.resources,
  org.eclipse.emf.ecore.xmi.impl,
  org.eclipse.ocl.ecore
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/AdaptationControl.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/AdaptationControl.java
index 818d44ff..dc665969 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/AdaptationControl.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/AdaptationControl.java
@@ -4,8 +4,8 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.Properties;
 
-import org.apache.log4j.BasicConfigurator;
 import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.xmi.DanglingHREFException;
@@ -45,6 +45,7 @@ public class AdaptationControl {
 
     private AdaptationProcessModelLoader adaptationProcessModelLoader = AdaptationProcessModelLoader.getInstance();
     private IPerformanceDataRepositoryHandler perfDataRepoHandler;
+    private DmmModelActionHandler dmmModelActionHandler;
     private AdaptationProcess adaptationProcess = null;
     private TacticExecutor executor = null;
     private IModelAnalyzer modelAnalyzer = null;
@@ -78,7 +79,8 @@ public class AdaptationControl {
             perfDataRepoHandler.load(performanceDataRepoXmiFilePath);
 
             // set handlers
-            executor = new TacticExecutor(new DmmModelActionHandler());
+            dmmModelActionHandler = new DmmModelActionHandler();
+            executor = new TacticExecutor(dmmModelActionHandler);
             modelAnalyzer = new PcmModelAnalyzer();
             evaluator = new WeightingFunctionEvaluator();
         } catch (IOException e) {
@@ -89,8 +91,7 @@ public class AdaptationControl {
 
     /* (non-Javadoc) refactor this after integration of QL Engine */
     private static void prepare(String propertiesfile) {
-        BasicConfigurator.configure();
-        Logger.getRootLogger().setLevel(Level.DEBUG);
+        LogManager.getRootLogger().setLevel(Level.DEBUG);
 
         AdaptationControl adaptationController = new AdaptationControl();
         // Initialize controller (load models, create Action perfDataRepoHandler, etc.)
@@ -118,16 +119,15 @@ public class AdaptationControl {
 
             applyTactic(currentTactic);
             analyzeModel();
-            processResults();
             evaluate();
 
             // save results
             try {
                 logger.debug("Saving tactics evaluation results...");
-                adaptationProcessModelLoader.saveAll();
+                AdaptationProcessModelLoader.saveAll();
             } catch (IOException e) {
                 if (e.getCause() instanceof DanglingHREFException)
-                    logger.error("Error while persisting results because of dangling references. Probably not all changes have been saved");
+                    logger.error("Error while persisting results because of dangling references. Probably not all changes have been saved.");
                 else
                     logger.error("Error while persisting evaluation results.", e);
             }
@@ -162,23 +162,16 @@ public class AdaptationControl {
     }
 
     /**
-     * Choose a suitable model analyzer and analyze the reconfigured model.
+     * Choose a suitable model analyzer and analyze the reconfigured model. Next, process the
+     * results of the applied weighted tactic. For example, store the results in a performance data
+     * repository.
      */
     public void analyzeModel() {
         modelAnalyzer.analyze();
-    }
-
-    /**
-     * Process the results of the applied weighted tactic. For example, store the results in a
-     * performance data repository.
-     * 
-     * @param tactic
-     */
-    public void processResults() {
-
         WeightedTactic tactic = TacticsHistory.getInstance().getLatestAppliedTactic();
-        
+
         if (TacticsHistory.getInstance().wasLatestTacticSuccessful()) {
+            // TODO Call model analysis
             Impact latestImpact = perfDataRepoHandler.getLatestImpact(tactic);
             if (latestImpact != null)
                 tactic.setLastImpact(latestImpact);
@@ -194,25 +187,21 @@ public class AdaptationControl {
      * @param tactic
      */
     public void evaluate() {
-        
         WeightedTactic tactic = TacticsHistory.getInstance().getLatestAppliedTactic();
-        
-        if (TacticsHistory.getInstance().wasLatestTacticSuccessful()) {
-        
+
+        if (!TacticsHistory.getInstance().wasLatestTacticSuccessful())
+            evaluator.punishTactic(tactic);
+        else {
             // Check if tactic has made anything stupid, i.e., violates the objectives. If yes, undo
             // and/or assign bad weight.
-            if (evaluator.degradationDetected(tactic)) {
-                logger.warn("Degradation detected for tactic " + tactic.getName());
-                // undo tactic
-                // assign negative weight
+            if (evaluator.qosDegradationDetected(tactic)) {
+                logger.warn("QoS degradation detected after applying tactic " + tactic.getName());
+                executor.undoPreviousTactic();
+                evaluator.punishTactic(tactic);
+            } else {
+                evaluator.evaluate(tactic);
             }
-    
-            // If everything is fine, assign new weight.
-            evaluator.evaluate(tactic);
-        } else {
-            evaluator.punishTactic(tactic);
         }
-         
     }
 
     /**
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/TacticExecutor.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/TacticExecutor.java
index 6710af5d..49865c04 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/TacticExecutor.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/TacticExecutor.java
@@ -1,20 +1,27 @@
 package edu.kit.ipd.descartes.adaptation;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.log4j.Logger;
 import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.change.ChangeDescription;
 
 import edu.kit.ipd.descartes.adaptation.exceptions.InvalidAdaptationPlan;
 import edu.kit.ipd.descartes.adaptation.exceptions.OperationNotPerformedException;
+import edu.kit.ipd.descartes.adaptation.model.dmm.AdaptationProcessModelLoader;
 import edu.kit.ipd.descartes.adaptation.model.dmm.util.OclEvaluationHelper;
+import edu.kit.ipd.descartes.adaptation.util.BlueYonderExperimentTracker;
+import edu.kit.ipd.descartes.adaptation.util.IObserver;
 import edu.kit.ipd.descartes.mm.adaptation.AbstractControlFlowElement;
 import edu.kit.ipd.descartes.mm.adaptation.Action;
 import edu.kit.ipd.descartes.mm.adaptation.ActionReference;
 import edu.kit.ipd.descartes.mm.adaptation.AdaptationPlan;
 import edu.kit.ipd.descartes.mm.adaptation.BranchAction;
 import edu.kit.ipd.descartes.mm.adaptation.LoopAction;
+import edu.kit.ipd.descartes.mm.adaptation.Parameter;
 import edu.kit.ipd.descartes.mm.adaptation.StartAction;
 import edu.kit.ipd.descartes.mm.adaptation.StopAction;
 import edu.kit.ipd.descartes.mm.adaptation.WeightedTactic;
@@ -24,14 +31,17 @@ import edu.kit.ipd.descartes.mm.resourcelandscape.ResourcelandscapePackage;
 
 public class TacticExecutor {
 
-	static Logger logger = Logger.getLogger(TacticExecutor.class);
+	private static Logger logger = Logger.getLogger(TacticExecutor.class);
+	private List<IObserver> observers = new ArrayList<IObserver>(); 
 
 	private IActionHandler dmmModelActionHandler;
 	private TacticsHistory hist;
+	private ChangeDescription changeDescription = null;
 
 	public TacticExecutor(IActionHandler actionExecutor) {
 		dmmModelActionHandler = actionExecutor;
 		hist = TacticsHistory.getInstance();
+		register(BlueYonderExperimentTracker.getInstance()); // TODO implement clean observer pattern
 	}
 	
 	/**
@@ -43,6 +53,7 @@ public class TacticExecutor {
      */
     public void applyTactic(WeightedTactic weightedTactic) {
     	try {
+    	    AdaptationProcessModelLoader.startRecording();
             executeAdaptationPlan(weightedTactic.getUsedTactic().getImplementedPlan());
             logger.info("Tactic " + weightedTactic.getUsedTactic().getName()
                     + " successfully applied.");
@@ -50,6 +61,7 @@ public class TacticExecutor {
     
             // if this point is reached everything was fine, so persist them (save the models)
             dmmModelActionHandler.persistActions();
+            changeDescription = AdaptationProcessModelLoader.endRecording();
             return;
         } catch (OperationNotPerformedException e) {
     		logger.error("Tactic " + weightedTactic.getUsedTactic().getName()
@@ -141,10 +153,12 @@ public class TacticExecutor {
 
     private void execLoopAction(AbstractControlFlowElement loopAction) throws OperationNotPerformedException {
         LoopAction loop = (LoopAction) loopAction;
-        int numberOfIterations = loop.getCounter();
+        Parameter counter = loop.getCounter();
+        int counterValue = Integer.parseInt(counter.getValue());
+        
         int i = 1;
     
-        while (i <= numberOfIterations) {
+        while (i <= counterValue) {
             logger.debug("Executing iteration " + i + " of loop action " + loop.getBody().getName());
             executeAdaptationPlan(loop.getBody());
             i++;
@@ -172,4 +186,20 @@ public class TacticExecutor {
         return invariant;
     }
 
+    public void undoPreviousTactic() {
+        logger.info("Reverting model to previous state");
+        for(IObserver o : observers)
+            o.undo();
+        if (changeDescription != null)
+            changeDescription.apply();
+    }
+    
+    public void register(IObserver observer) {
+        observers.add(observer);
+    }
+    
+    public void remove(IObserver observer) {
+        observers.remove(observer);
+    }
+
 }
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/IEvaluator.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/IEvaluator.java
index f9b22b23..eb0deeda 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/IEvaluator.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/IEvaluator.java
@@ -1,5 +1,6 @@
 package edu.kit.ipd.descartes.adaptation.evaluation;
 
+import edu.kit.ipd.descartes.mm.adaptation.Objective;
 import edu.kit.ipd.descartes.mm.adaptation.WeightedTactic;
 
 /**
@@ -13,18 +14,19 @@ public interface IEvaluator {
     /**
      * Evaluates the impact of a {@link WeightedTactic}.
      * 
-     * @param wt
+     * @param tactic
      *            The weighted tactic to evaluate
      */
-    public void evaluate(WeightedTactic wt);
+    public void evaluate(WeightedTactic tactic);
 
     /**
-     * Checks if any QoS property has degraded significantly after applying the tactic.
+     * Checks if any QoS property has degraded significantly, i.e. any {@link Objective}has been
+     * violated, after applying the tactic.
      * 
      * @param tactic
      * @return
      */
-    public boolean degradationDetected(WeightedTactic tactic);
+    public boolean qosDegradationDetected(WeightedTactic tactic);
 
     /**
      * Reduces the weight of the given tactic.
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/WeightingFunctionEvaluator.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/WeightingFunctionEvaluator.java
index a124c0ad..61e8eda5 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/WeightingFunctionEvaluator.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/WeightingFunctionEvaluator.java
@@ -19,7 +19,7 @@ import edu.kit.ipd.descartes.perfdatarepo.Result;
  */
 public class WeightingFunctionEvaluator implements IEvaluator {
 
-    private static final String RESPONSETIME_IDENTIFIER = "time";
+    private static final String RESPONSETIME_IDENTIFIER = "ResponseTime";
     private IWeightingFunction calculator = null;
     private static Logger logger = Logger.getLogger(WeightingFunctionEvaluator.class);
 
@@ -56,7 +56,7 @@ public class WeightingFunctionEvaluator implements IEvaluator {
         tactic.setCurrentWeight(newWeight);
     }
 
-    public boolean degradationDetected(WeightedTactic tactic) {
+    public boolean qosDegradationDetected(WeightedTactic tactic) {
         Impact impact = tactic.getLastImpact();
         Result before = impact.getBefore();
         Result after = impact.getAfter();
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/ResourceEfficiency.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/BlueYonderResourceEfficiencyWeightingFunction.java
similarity index 91%
rename from edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/ResourceEfficiency.java
rename to edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/BlueYonderResourceEfficiencyWeightingFunction.java
index 831f34c4..3d1ea4e0 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/ResourceEfficiency.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/evaluation/weightingfunction/BlueYonderResourceEfficiencyWeightingFunction.java
@@ -9,14 +9,14 @@ import edu.kit.ipd.descartes.perfdatarepo.Impact;
 import edu.kit.ipd.descartes.perfdatarepo.MetricValue;
 import edu.kit.ipd.descartes.perfdatarepo.Result;
 
-public class ResourceEfficiency implements IWeightingFunction {
+public class BlueYonderResourceEfficiencyWeightingFunction implements IWeightingFunction {
 
     private static final String UTIL_IDENTIFIER = "Util";
     private static final double THRESHOLD = 0.05;
     private WeightingFunction function = null;
-    private static Logger logger = Logger.getLogger(ResourceEfficiency.class);
+    private static Logger logger = Logger.getLogger(BlueYonderResourceEfficiencyWeightingFunction.class);
 
-//    public ResourceEfficiency() {
+//    public BlueYonderResourceEfficiencyWeightingFunction() {
 //        AbstractEcoreModelLoader loader = PerformanceDataRepositoryModelLoader.getInstance();
 //        String pdrLocation = loader.getCurrentModelUri().toFileString();
 //        logger.debug("Initializing query engine adaptor for performance data repository " + pdrLocation);
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/AdaptationProcessModelLoader.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/AdaptationProcessModelLoader.java
index 6993974f..061b80ae 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/AdaptationProcessModelLoader.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/AdaptationProcessModelLoader.java
@@ -8,6 +8,8 @@ import java.util.Map;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.change.ChangeDescription;
+import org.eclipse.emf.ecore.change.util.ChangeRecorder;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
@@ -29,6 +31,7 @@ public class AdaptationProcessModelLoader extends AbstractEcoreModelLoader {
             AdaptationpointsPackage.eINSTANCE, ContainerrepositoryPackage.eINSTANCE, AdaptationPackage.eINSTANCE };
 
     private static AdaptationProcessModelLoader instance = null;
+    private static ChangeRecorder recorder = null;
 
     public static AdaptationProcessModelLoader getInstance() {
         if (instance == null) {
@@ -41,9 +44,9 @@ public class AdaptationProcessModelLoader extends AbstractEcoreModelLoader {
         initializeResourceSet(new ResourceSetImpl());
     }
 
-    public void saveAll() throws IOException {
+    public static void saveAll() throws IOException {
 
-        EList<Resource> resources = getResourceSet().getResources();
+        EList<Resource> resources = getInstance().getResourceSet().getResources();
         Map<String, String> options = new HashMap<String, String>();
         options.put(XMIResource.OPTION_SCHEMA_LOCATION, Boolean.TRUE.toString());
         options.put(XMIResource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER, Boolean.TRUE.toString());
@@ -54,7 +57,8 @@ public class AdaptationProcessModelLoader extends AbstractEcoreModelLoader {
                 res.save(options);
             } catch (IOException e) {
                 if (e.getCause() instanceof DanglingHREFException) {
-                    String newMessage = "Error while persisting " + res.getURI() + " because of dangling references. Probably not all changes have been saved.\n";
+                    String newMessage = "Error while persisting " + res.getURI()
+                            + " because of dangling references. Probably not all changes have been saved.\n";
                     throw new IOException(newMessage + e.getMessage(), e.getCause());
                 }
             }
@@ -88,4 +92,17 @@ public class AdaptationProcessModelLoader extends AbstractEcoreModelLoader {
     public AdaptationProcess load(URI uri) {
         return (AdaptationProcess) super.load(uri);
     }
+
+    public static void startRecording() {
+        recorder = new ChangeRecorder(getInstance().getResourceSet());
+    }
+    
+    public static ChangeDescription endRecording() {
+        return recorder.endRecording();
+    }
+
+    public static void endRecordingAndRollback() {
+        recorder.endRecording().apply();
+        
+    }
 }
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/DmmModelActionHandler.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/DmmModelActionHandler.java
index a5201147..7885def7 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/DmmModelActionHandler.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/DmmModelActionHandler.java
@@ -2,6 +2,7 @@ package edu.kit.ipd.descartes.adaptation.model.dmm;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Random;
 
 import org.apache.log4j.Logger;
 import org.eclipse.emf.common.util.EList;
@@ -168,7 +169,7 @@ public class DmmModelActionHandler implements IActionHandler {
         return invariant;
     }
 
-    private void migrate(Entity adaptableEntity, VariationType targets, AdaptationScope adaptationScope) {
+    private void migrate(Entity adaptableEntity, VariationType targets, AdaptationScope adaptationScope) throws OperationNotPerformedException {
         switch (adaptableEntity.eClass().getClassifierID()) {
         case ResourcelandscapePackage.RUNTIME_ENVIRONMENT:
             logger.debug("Starting migration of a runtime environment referring to container template "
@@ -184,25 +185,30 @@ public class DmmModelActionHandler implements IActionHandler {
             EList<Entity> migrationTargets = ((SetOfConfigurations) targets).getVariants();
 
             RuntimeEnvironment migratedEntity = (RuntimeEnvironment) adaptableEntity;
-            Container parent = migratedEntity.getContainedIn();
+            Container source = migratedEntity.getContainedIn();
             // TODO Here you could implement something more sophisticated
             // e.g., choose the container with the lowest util.
-            Entity randomTarget = randomTarget(migrationTargets, parent);
-            if (!(randomTarget instanceof Container))
+            Entity target = randomTarget(migrationTargets, source);
+            
+            if (!(target instanceof Container))
                 throw new IllegalArgumentException(
                         "Migration could not be performed because target is not a container!");
-
-            Container targetContainer = (Container) randomTarget;
+            Container targetContainer = (Container) target;
+            
+            if (migrationTargets.size() == 1 && EcoreUtil.equals(source, target))
+                throw new OperationNotPerformedException("Migration operation not executed because source and destination are identical.");
 
             targetContainer.getContains().add(migratedEntity);
-            parent.getContains().remove(migratedEntity);
+            source.getContains().remove(migratedEntity);
             break;
         case ContainerrepositoryPackage.CONTAINER_TEMPLATE:
-            logger.debug("Starting migration of a container referring to container template "
-                    + adaptableEntity.getName() + " (ID: " + adaptableEntity.getId() + ")");
-            ContainerTemplate ct = (ContainerTemplate) adaptableEntity;
-            Container c = DmmModelActionHelper.findSuitableContainer(ct, adaptationScope);
-            migrate(c, targets, adaptationScope);
+//            logger.debug("Starting migration of a container referring to container template "
+//                    + adaptableEntity.getName() + " (ID: " + adaptableEntity.getId() + ")");
+            ContainerTemplate containerTemplate = (ContainerTemplate) adaptableEntity;
+            Container migratedContainer = DmmModelActionHelper.findSuitableContainer(containerTemplate, adaptationScope, targets);
+            if (migratedContainer == null)
+                throw new OperationNotPerformedException("Could not execute migration operation because no suitable migration candidate found");
+            migrate(migratedContainer, targets, adaptationScope);
             break;
         default:
             throw new IllegalArgumentException("No implemented migrate operation for " + adaptableEntity);
@@ -211,11 +217,13 @@ public class DmmModelActionHandler implements IActionHandler {
     }
 
     private Entity randomTarget(EList<Entity> destinations, Entity source) {
-        Entity destination = null;
-
-        while (destination == null || EcoreUtil.equals(destination, source)) {
-            int destSize = destinations.size() - 1;
-            int destNumber = (int) Math.round(Math.random() * destSize);
+        if (destinations.size() == 1)
+            return destinations.get(0);
+        
+        Entity destination = destinations.get(0);
+        Random randomGenerator = new Random();
+        while (EcoreUtil.equals(destination, source)) {
+            int destNumber = randomGenerator.nextInt(destinations.size());
             destination = destinations.get(destNumber);
         }
 
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/util/DmmModelActionHelper.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/util/DmmModelActionHelper.java
index ea7338fe..203cd216 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/util/DmmModelActionHelper.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/dmm/util/DmmModelActionHelper.java
@@ -3,14 +3,16 @@ package edu.kit.ipd.descartes.adaptation.model.dmm.util;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Random;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.util.EcoreUtil;
 
 import edu.kit.ipd.descartes.core.AdaptableEntity;
+import edu.kit.ipd.descartes.core.Entity;
 import edu.kit.ipd.descartes.core.NamedElement;
 import edu.kit.ipd.descartes.mm.adaptation.AdaptationScope;
+import edu.kit.ipd.descartes.mm.adaptationpoints.SetOfConfigurations;
+import edu.kit.ipd.descartes.mm.adaptationpoints.VariationType;
 import edu.kit.ipd.descartes.mm.containerrepository.ContainerTemplate;
 import edu.kit.ipd.descartes.mm.resourceconfiguration.ConfigurationSpecification;
 import edu.kit.ipd.descartes.mm.resourceconfiguration.ResourceconfigurationPackage;
@@ -26,19 +28,19 @@ public class DmmModelActionHelper {
      * @return the {@link EObject} of the model instance that contains {@code aEntity}
      */
     public static EObject findParentConfiguredInstance(AdaptableEntity aEntity) {
-        
+
         ConfigurationSpecification c = null;
-        
+
         if (aEntity.eClass().getClassifierID() == ResourceconfigurationPackage.NUMBER_OF_PARALLEL_PROCESSING_UNITS)
             c = (ConfigurationSpecification) aEntity.eContainer().eContainer();
         else if (aEntity.eClass().getClassifierID() == ResourceconfigurationPackage.PASSIVE_RESOURCE_CAPACITY)
             c = (ConfigurationSpecification) aEntity.eContainer();
         else
             return null;
-               
+
         return c.eContainer();
     }
-    
+
     /**
      * Query for a {@link Container} with similar specs
      * 
@@ -50,15 +52,14 @@ public class DmmModelActionHelper {
             Container comparatorContainer) {
 
         List<Container> resultList = new ArrayList<Container>();
-        
+
         for (Container c : candidates) {
 
             /*
              * Make sure we find a RuntimeEnvironment with 1. a DIFFERENT ID 2. similar properties
              * and 3. name must contain COPY_MARK to make sure we remove only a copy
              */
-            if (c.getId() != comparatorContainer.getId() 
-                    && c.getName().contains(DmmModelChanger.COPY_MARK) 
+            if (c.getId() != comparatorContainer.getId() && c.getName().contains(DmmModelChanger.COPY_MARK)
                     && EcoreUtil.equals(comparatorContainer.getConfigSpec(), c.getConfigSpec())
                     && EcoreUtil.equals(comparatorContainer.getTemplate(), c.getTemplate())) {
                 resultList.add(c);
@@ -66,11 +67,10 @@ public class DmmModelActionHelper {
         }
         return resultList;
     }
-    
+
     /**
-     * Query for a {@link RuntimeEnvironment} that 
-     * has been copied (contains {@link DmmModelChanger.COPY_MARK}) and refers to
-     * the same ContainerTemplate.
+     * Query for a {@link RuntimeEnvironment} that has been copied (contains
+     * {@link DmmModelChanger.COPY_MARK}) and refers to the same ContainerTemplate.
      * 
      * @param candidates
      * @param comparatorRE
@@ -81,20 +81,19 @@ public class DmmModelActionHelper {
 
         for (RuntimeEnvironment re : candidates) {
 
-            if (re.getId() != comparatorRE.getId() 
-                    && re.getName().contains(DmmModelChanger.COPY_MARK) 
-//                    && EcoreUtil.equals(comparatorRE.getConfigSpec(), re.getConfigSpec())
-//                    && EcoreUtil.equals(comparatorRE.getContainedIn(), re.getContainedIn())
+            if (re.getId() != comparatorRE.getId() && re.getName().contains(DmmModelChanger.COPY_MARK)
+            // && EcoreUtil.equals(comparatorRE.getConfigSpec(), re.getConfigSpec())
+            // && EcoreUtil.equals(comparatorRE.getContainedIn(), re.getContainedIn())
                     && EcoreUtil.equals(comparatorRE.getTemplate(), re.getTemplate())) {
                 return re;
             }
         }
         return null;
     }
-    
+
     public static NamedElement findEntityWithName(NamedElement element, String name) {
-       
-        if(element.getName().equals(name))
+
+        if (element.getName().equals(name))
             return element;
         else {
             NamedElement result = null;
@@ -107,61 +106,64 @@ public class DmmModelActionHelper {
             return result;
         }
     }
-    
-    /** Queries for all Container instances that have a similar configuration
-     *  specification as Container <code>c</code>. Similar configuration 
-     *  specification meas that ContainerTemplate and ConfigurationSpecification
-     *  are equal.
+
+    /**
+     * Queries for all Container instances that have a similar configuration specification as
+     * Container <code>c</code>. Similar configuration specification meas that ContainerTemplate and
+     * ConfigurationSpecification are equal.
      * 
-     * @param c 
+     * @param c
      * @return A list of Container instances
      */
     public static List<Container> queryContainersWithSimilarSpecs(Container c) {
-           List<Container> result = queryContainersWithSimilarTemplate(c);
-           return filterContainersWithSimilarSpecs(result, c);
+        List<Container> result = queryContainersWithSimilarTemplate(c);
+        return filterContainersWithSimilarSpecs(result, c);
     }
 
-    /**Queries for a RuntimeEnvironment whose ContainerTemplate is similar to the
-     * given RuntimeEnvironment <code>re</code> and whose capacity is below <code>maxValue</code>.
+    /**
+     * Queries for a RuntimeEnvironment whose ContainerTemplate is similar to the given
+     * RuntimeEnvironment <code>re</code> and whose capacity is below <code>maxValue</code>.
      * 
      * @param re
      * @param maxValue
      * @return
      */
     public static RuntimeEnvironment queryUnfilledRuntimeEnvironment(RuntimeEnvironment re, double maxValue) {
-        //TODO SEAMS specific helper function! Generalize!
-        String queryExpression = "RuntimeEnvironment.allInstances()->select( re | re.template = self.template )->select(re | re.configSpec.oclAsType(resourceconfiguration::ActiveResourceSpecification).processingResourceSpecifications->forAll(nrOfParProcUnits.number < "+maxValue+"))";
-        List<EObject> resultList = new ArrayList<EObject>(
-                OclEvaluationHelper.query(re, queryExpression, re.eClass()));
-        
-        if (resultList.size()>0)
+        // TODO SEAMS specific helper function! Generalize!
+        String queryExpression = "RuntimeEnvironment.allInstances()->select( re | re.template = self.template )->select(re | re.configSpec.oclAsType(resourceconfiguration::ActiveResourceSpecification).processingResourceSpecifications->forAll(nrOfParProcUnits.number < "
+                + maxValue + "))";
+        List<EObject> resultList = new ArrayList<EObject>(OclEvaluationHelper.query(re, queryExpression, re.eClass()));
+
+        if (resultList.size() > 0)
             return (RuntimeEnvironment) resultList.get(0);
         else
             return null;
     }
-    
+
     public static RuntimeEnvironment queryNotEmptyRuntimeEnvironment(RuntimeEnvironment re, double minValue) {
         // TODO SEAMS specific helper function! Generalize!
-        String queryExpression = "RuntimeEnvironment.allInstances()->select( re | re.template = self.template and not re.configSpec->isEmpty() )->select(re | re.configSpec.oclAsType(resourceconfiguration::ActiveResourceSpecification).processingResourceSpecifications->forAll(nrOfParProcUnits.number > "+minValue+"))";
-        List<EObject> resultList = new ArrayList<EObject>(
-                OclEvaluationHelper.query(re, queryExpression, re.eClass()));
-        
-        if (resultList.size()>0)
+        String queryExpression = "RuntimeEnvironment.allInstances()->select( re | re.template = self.template and not re.configSpec->isEmpty() )->select(re | re.configSpec.oclAsType(resourceconfiguration::ActiveResourceSpecification).processingResourceSpecifications->forAll(nrOfParProcUnits.number > "
+                + minValue + "))";
+        List<EObject> resultList = new ArrayList<EObject>(OclEvaluationHelper.query(re, queryExpression, re.eClass()));
+
+        if (resultList.size() > 0)
             return (RuntimeEnvironment) resultList.get(0);
         else
             return null;
     }
 
-    /**Queries for all Container instances referring to the same 
-     * ContainerTemplate as the given container.
+    /**
+     * Queries for all Container instances referring to the same ContainerTemplate as the given
+     * container.
      * 
-     * @param container The container to look for
+     * @param container
+     *            The container to look for
      * @return All Container Instances referring to the same ContainerTemplate
      */
     public static List<Container> queryContainersWithSimilarTemplate(Container container) {
         String queryExpression = "RuntimeEnvironment.allInstances()->select( re | re.template = self.template and re.id <> self.id)";
-        List<EObject> tempList = new ArrayList<EObject>(
-                OclEvaluationHelper.query(container, queryExpression, container.eClass()));
+        List<EObject> tempList = new ArrayList<EObject>(OclEvaluationHelper.query(container, queryExpression,
+                container.eClass()));
         List<Container> resultList = new ArrayList<Container>(tempList.size());
         for (Iterator<EObject> iterator = tempList.iterator(); iterator.hasNext();) {
             Container temp = (Container) iterator.next();
@@ -170,40 +172,82 @@ public class DmmModelActionHelper {
         return resultList;
     }
 
-    
-    /**Filter the given {@code containerList} for RuntimeEnvironments and cast 
-     * them to {@link RuntimeEnvironment}
+    /**
+     * Filter the given {@code containerList} for RuntimeEnvironments and cast them to
+     * {@link RuntimeEnvironment}
+     * 
      * @param containerList
      * @return a list of RuntimeEnvironments
      */
     public static List<RuntimeEnvironment> filterAndConvertRuntimeEnvironment(List<Container> containerList) {
         List<RuntimeEnvironment> result = new ArrayList<RuntimeEnvironment>();
-        
+
         for (Container container : containerList) {
-            if (container.eClass().getClassifierID()  == ResourcelandscapePackage.RUNTIME_ENVIRONMENT)
+            if (container.eClass().getClassifierID() == ResourcelandscapePackage.RUNTIME_ENVIRONMENT)
                 result.add((RuntimeEnvironment) container);
         }
-        
+
         return result;
     }
-    
-    /**Find a container that refers to the given {@link ContainerTemplate} and that suits the
-     * given {@link AdaptationScope}.
+
+    /**
+     * Find a container that refers to the given {@link ContainerTemplate} and that suits the given
+     * {@link AdaptationScope}.
      * 
      * @param containerTemplate
      * @param adaptationScope
+     * @param targets 
      * @return
      */
-    public static Container findSuitableContainer(ContainerTemplate containerTemplate, AdaptationScope adaptationScope) {
+    public static Container findSuitableContainer(ContainerTemplate containerTemplate, AdaptationScope adaptationScope, VariationType targets) {
+        // Get all containers referring to the template
         List<Container> containers = containerTemplate.getReferringContainers();
-        Random randomGenerator = new Random();
+        List<Entity> variants = ((SetOfConfigurations) targets).getVariants();
+        
+        // Prepare the list of migration targets
+        List<Container> migrationTargets = new ArrayList<Container>();
+        for (Entity entity : variants) {
+            migrationTargets.add((Container) entity);
+        }
+        
+        for (Container container : containers) {
+            
+            // Remove container from migration targets
+            List<Container> migrationTargetCandidates = excludeSelfContainmentFromTargetList(container, migrationTargets);
+            
+            // If there are migration targets left
+            if (migrationTargetCandidates.size() > 0) {
+                switch (adaptationScope) {
+                case RANDOM:
+                    return container;
+                default:
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
 
-        switch (adaptationScope) {
-        case RANDOM:
-            return containers.get(randomGenerator.nextInt(containers.size()));
-        default:
+    /**
+     * Returns a list of {@code targets} excluding the target the container {@code self} is already
+     * contained in.
+     * 
+     * @param self
+     * @param targets
+     * @return
+     */
+    public static List<Container> excludeSelfContainmentFromTargetList(Container self, List<Container> targets) {
+        if (self == null || targets == null)
             return null;
+
+        List<Container> resultList = new ArrayList<Container>();
+
+        for (Container target : targets) {
+            if (!EcoreUtil.equals(target, self.eContainer()))
+                resultList.add(target);
         }
+
+        return resultList;
     }
-    
+
 }
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/perfdatarepo/ObservationRepositoryHandler.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/perfdatarepo/ObservationRepositoryHandler.java
index 473f6868..eb62e0cc 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/perfdatarepo/ObservationRepositoryHandler.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/model/perfdatarepo/ObservationRepositoryHandler.java
@@ -16,7 +16,7 @@ import edu.kit.ipd.descartes.ql.models.repository.ObservationRepository;
 
 public class ObservationRepositoryHandler implements IPerformanceDataRepositoryHandler {
 
-    private BlueYonderExperimentTracker tracker = new BlueYonderExperimentTracker();
+    private BlueYonderExperimentTracker tracker = BlueYonderExperimentTracker.getInstance();
     private static Logger logger = Logger.getLogger(ObservationRepositoryHandler.class);
     private AbstractEcoreModelLoader modelLoader = null;
     private ObservationRepository repository = null;
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/BlueYonderExperimentTracker.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/BlueYonderExperimentTracker.java
index 6d29bc93..aa652822 100644
--- a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/BlueYonderExperimentTracker.java
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/BlueYonderExperimentTracker.java
@@ -1,32 +1,77 @@
 package edu.kit.ipd.descartes.adaptation.util;
 
+import edu.kit.ipd.descartes.adaptation.TacticExecutor;
 import edu.kit.ipd.descartes.mm.adaptation.WeightedTactic;
 
-public class BlueYonderExperimentTracker {
+public class BlueYonderExperimentTracker implements IObserver {
 
     private static final String RESULT_NAME = "Xreq-par-$1Yps-desc4-gw-desc3-db-desc2-predict-Z ps=$2-req=5-size=50000";
+    private static BlueYonderExperimentTracker instance = null;
     private int psOnDescOne = 0;
     private int psOnDescFour = 1;
+    private int psOnDescOneBackup;
+    private int psOnDescFourBackup;
+
+    public void setSubject(TacticExecutor te) {
+        te.register(this);
+    }
 
     public String getCurrentExperimentName() {
         String result = RESULT_NAME;
-        
+
         if (psOnDescOne > 0)
             result = RESULT_NAME.replace("$1", psOnDescOne + "ps-desc1-");
         else
             result = RESULT_NAME.replace("$1", "");
-        
+
         result = result.replace("$2", Integer.toString(psOnDescFour));
         return result;
     }
 
     public void track(WeightedTactic tactic) {
         String execTacticName = tactic.getName();
+        backupValues();
 
-        if (execTacticName.contains("Low")) {
+        if (execTacticName.contains("Consolidate")) {
+            int loopCount = Integer.parseInt(tactic.getUsedTactic().getInputParams().get(0).getValue());
+            if (execTacticName.contains("Low")) {
+                psOnDescFour = psOnDescFour - loopCount;
+                psOnDescOne = psOnDescOne + loopCount;
+            } else if (execTacticName.contains("High")) {
+                psOnDescOne = psOnDescOne - loopCount;
+                psOnDescFour = psOnDescFour + loopCount;
+            }
+        } else if (execTacticName.contains("Low")) {
             psOnDescOne++;
+            assert psOnDescFour > 0;
+            if (execTacticName.contains("Migrate"))
+                psOnDescFour--;
         } else if (execTacticName.contains("High")) {
             psOnDescFour++;
+            assert psOnDescOne > 0;
+            if (execTacticName.contains("Migrate"))
+                psOnDescOne--;
         }
     }
+
+    private void backupValues() {
+        psOnDescOneBackup = psOnDescOne;
+        psOnDescFourBackup = psOnDescFour;
+    }
+
+    @Override
+    public void undo() {
+        resetValues();
+    }
+
+    private void resetValues() {
+        psOnDescOne = psOnDescOneBackup;
+        psOnDescFour = psOnDescFourBackup;
+    }
+
+    public static BlueYonderExperimentTracker getInstance() {
+        if (instance == null)
+            instance = new BlueYonderExperimentTracker();
+        return instance;
+    }
 }
diff --git a/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/IObserver.java b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/IObserver.java
new file mode 100644
index 00000000..42e15cd8
--- /dev/null
+++ b/edu.kit.ipd.descartes.adaptation/src/edu/kit/ipd/descartes/adaptation/util/IObserver.java
@@ -0,0 +1,7 @@
+package edu.kit.ipd.descartes.adaptation.util;
+
+public interface IObserver {
+    
+    public void undo();
+
+}
-- 
GitLab