Index: /branches/2915-AbsoluteSymbol/Build.cmd
===================================================================
--- /branches/2915-AbsoluteSymbol/Build.cmd	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/Build.cmd	(revision 16240)
@@ -1,104 +1,2 @@
 @ECHO OFF
-
-SET CLEANBEFOREBUILD=
-
-SET SELECTED=
-SET CONFIGURATION=
-SET PLATFORM=
-
-IF "%~1"=="" GOTO :prompt_solution
-
-SET SELECTED=%1
-IF NOT EXIST %SELECTED% (
-  ECHO Solution file %SELECTED% could not be found.
-  GOTO :end
-)
-ECHO Building solution %SELECTED% ...
-GOTO :config_selection
-
-:prompt_solution
-SET /A COUNT=0
-FOR /F "tokens=*" %%A IN ('dir /B *.sln') DO (
-  CALL :forloopbody "%%A"
-)
-
-IF "%COUNT%"=="1" (
-  SET SELECTED=%SOLUTIONS.1%
-  ECHO Building %SOLUTIONS.1% as it is the only solution that was found ...
-  GOTO :config_selection
-)
-
-ECHO Found the following solutions:
-FOR /F "tokens=2* delims=.=" %%A IN ('SET SOLUTIONS.') DO ECHO %%A = %%B
-ECHO.
-SET /P SOLUTIONINDEX=Which solution to build? Type the number: 
-
-SET SELECTED=""
-FOR /F "tokens=2* delims=.=" %%A IN ('SET SOLUTIONS.') DO (
-  IF "%%A"=="%SOLUTIONINDEX%" SET SELECTED=%%B
-)
-
-IF %SELECTED%=="" GOTO :eof
-
-:config_selection
-IF "%~2"==""  GOTO :prompt_config
-
-SET CONFIGURATION=%~2
-ECHO Building configuration %CONFIGURATION% ...
-GOTO :platform_selection
-
-:prompt_config
-SET /P CONFIGURATION=Which configuration to build [Release]: 
-IF "%CONFIGURATION%"=="" SET CONFIGURATION=Release
-
-:platform_selection
-IF "%~3"=="" GOTO :prompt_platform
-  
-SET PLATFORM=%~3
-ECHO Building platform %PLATFORM% ...
-GOTO :clean
-
-:prompt_platform
-SET /P PLATFORM=Which platform to build [Any CPU]: 
-IF "%PLATFORM%"=="" SET PLATFORM=Any CPU
-
-:clean
-IF "%~4"=="" GOTO :prompt_clean
-
-SET CLEANBEFOREBUILD=%~4
-GOTO :main
-
-:prompt_clean
-SET /P CLEANBEFOREBUILD=Would you like to clean before building [n]: 
-IF "%CLEANBEFOREBUILD%"=="" SET CLEANBEFOREBUILD=n
-
-:main
-REM First find the path to the msbuild.exe by performing a registry query
-FOR /F "tokens=1,3 delims=	 " %%A IN ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0"') DO (
-  IF "%%A"=="MSBuildToolsPath" SET MSBUILDPATH=%%B)
-
-REM Then execute msbuild to clean and build the solution
-REM Disable that msbuild creates a cache file of the solution
-SET MSBuildUseNoSolutionCache=1
-REM Run msbuild to clean and then build
-IF "%CLEANBEFOREBUILD%" NEQ "n" (
-  ECHO Cleaning ...
-  %MSBUILDPATH%msbuild.exe %SELECTED% /target:Clean /p:Configuration="%CONFIGURATION%",Platform="%PLATFORM%" /m:2 /nologo /verbosity:q /clp:ErrorsOnly
-)
-ECHO Building ...
-%MSBUILDPATH%msbuild.exe %SELECTED% /target:Build /p:Configuration="%CONFIGURATION%",Platform="%PLATFORM%" /m:2 /nologo /verbosity:q /clp:ErrorsOnly
-
-ECHO.
-ECHO DONE.
-
-:end
-
-PAUSE
-
-GOTO :eof
-
-REM This workaround is necessary so that COUNT gets reevaluated
-:forloopbody
-SET /A COUNT+=1
-SET SOLUTIONS.%COUNT%=%1
-GOTO :eof
+powershell.exe .\Build.ps1
Index: /branches/2915-AbsoluteSymbol/Build.ps1
===================================================================
--- /branches/2915-AbsoluteSymbol/Build.ps1	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/Build.ps1	(revision 16240)
@@ -0,0 +1,114 @@
+﻿# find ms build
+$programFilesX86Dir = ($Env:ProgramFiles, ${Env:ProgramFiles(x86)})[[bool]${Env:ProgramFiles(x86)}]
+$locations = @(
+  [System.IO.Path]::Combine($programFilesX86Dir, "Microsoft Visual Studio", "2017", "Enterprise", "MSBuild", "15.0", "Bin", "amd64")
+  [System.IO.Path]::Combine($programFilesX86Dir, "Microsoft Visual Studio", "2017", "Enterprise", "MSBuild", "15.0", "Bin")
+  [System.IO.Path]::Combine($programFilesX86Dir, "Microsoft Visual Studio", "2017", "Community", "MSBuild", "15.0", "Bin", "amd64")
+  [System.IO.Path]::Combine($programFilesX86Dir, "Microsoft Visual Studio", "2017", "Community", "MSBuild", "15.0", "Bin")
+  [System.IO.Path]::Combine($programFilesX86Dir, "Microsoft Visual Studio", "2017", "BuildTools", "MSBuild", "15.0", "Bin", "amd64"),
+  [System.IO.Path]::Combine($programFilesX86Dir, "Microsoft Visual Studio", "2017", "BuildTools", "MSBuild", "15.0", "Bin")
+)
+
+$msBuildPath = $undefined
+Foreach ($loc in $locations) {
+  $loc = [System.IO.Path]::Combine($loc, "MSBuild.exe")
+  If ([System.IO.File]::Exists($loc)) {
+    $msBuildPath = $loc
+    Break;
+  }
+}
+
+if ($msBuildPath -eq $undefined) {
+  "Could not locate MSBuild, ABORTING ..."
+} else {
+  $curPath = $MyInvocation.MyCommand.Path
+  $curDir = Split-Path $curPath
+
+  $slnFiles = Get-ChildItem $curDir -Filter *.sln
+
+  "Found the following solutions:"
+
+  ""
+
+  $slnFiles | % { $i = 0 } { ("  {0}. `"{1}`"" -f ($i + 1), $_.Name); $i++ }
+
+  ""
+
+  $success = $false
+
+  # query solution to build
+  $slnIndex = -1
+  $slnIndices = @()
+  Do {
+      $input = Read-Host "Which solution(s) to build? {1..$($slnFiles.Count)}"
+      $inputParts = $input -Split " "
+
+      Foreach ($part in $inputParts) {
+        If ($part -eq "") { Continue }
+
+        $success = [int]::TryParse($part, [ref]$slnIndex) -and ($slnIndex -gt 0) -and ($slnIndex -le $slnFiles.Count)
+
+        If ($success) {
+          $slnIndices += $slnIndex - 1
+        } Else {
+          $slnIndices = @()
+          Break
+        }
+      }
+  } While (-Not $success)
+
+  $slnIndices = $slnIndices | Select-Object -Unique
+
+  # query configuration to build
+  $config = "Release"
+  $input = Read-Host "Which configuration to build? [$($config)]"
+  $config = ($config, $input)[[bool]$input]
+
+  # query platform to build
+  $platform = "Any CPU"
+  $input = Read-Host "Which platform to build? [$($platform)]"
+  $platform = ($platform, $input)[[bool]$input]
+
+  # query clean desire
+  $clean = $false
+  Do {
+      $input = Read-Host "Would you like to clean before building? [y/N]"
+      $input = ([string]("n", $input)[[bool]$input]).ToLowerInvariant()
+      $success = $input -eq "n" -or ($clean = $input -eq "y")
+  } While (-Not $success)
+
+  ""
+
+  if ($clean) {
+    Foreach ($slnIndex in $slnIndices) {
+      $solution = $slnFiles[$slnIndex]
+      "Cleaning `"$($solution.Name)`" ..."
+      $args = @(
+        $solution.FullName,
+        "/t:Clean",
+        "/p:Configuration=`"$config`",Platform=`"$platform`"",
+        "/m", "/nologo", "/verbosity:q", "/clp:ErrorsOnly"
+      )
+      & $msBuildPath $args
+      "===== CLEAN FINISHED ====="
+    }
+  }
+
+  Foreach ($slnIndex in $slnIndices) {
+    $solution = $slnFiles[$slnIndex]
+    "Building `"$($solution.Name)`" ($config|$platform) ..."
+    $args = @(
+      $solution.FullName,
+      "/t:Build",
+      "/p:Configuration=`"$config`",Platform=`"$platform`"",
+      "/m", "/nologo", "/verbosity:q", "/clp:ErrorsOnly"
+    )
+    & $msBuildPath $args
+    "===== BUILD FINISHED ====="
+  }
+}
+
+""
+
+Write-Host -NoNewline "Press any key to continue ... "
+[void][System.Console]::ReadKey($true)
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/GBM/GradientBoostingRegressionAlgorithm.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/GBM/GradientBoostingRegressionAlgorithm.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/GBM/GradientBoostingRegressionAlgorithm.cs	(revision 16240)
@@ -210,5 +210,5 @@
     protected override void Run(CancellationToken cancellationToken) {
       // Set up the algorithm
-      if (SetSeedRandomly) Seed = new System.Random().Next();
+      if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
       var rand = new MersenneTwister((uint)Seed);
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs	(revision 16240)
@@ -186,5 +186,5 @@
     protected override void Run(CancellationToken cancellationToken) {
       // Set up the algorithm
-      if (SetSeedRandomly) Seed = new System.Random().Next();
+      if (SetSeedRandomly) Seed = Random.RandomSeedGenerator.GetSeed();
 
       // Set up the results display
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs	(revision 16240)
@@ -130,5 +130,8 @@
           // automatic determination of weights (all features should have variance = 1)
           this.weights = this.allowedInputVariables
-            .Select(name => 1.0 / dataset.GetDoubleValues(name, rows).StandardDeviationPop())
+            .Select(name => {
+              var pop = dataset.GetDoubleValues(name, rows).StandardDeviationPop();
+              return  pop.IsAlmost(0) ? 1.0 : 1.0/pop;
+            })
             .Concat(new double[] { 1.0 }) // no scaling for target variable
             .ToArray();
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NeuralNetwork/NeuralNetworkEnsembleModel.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NeuralNetwork/NeuralNetworkEnsembleModel.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NeuralNetwork/NeuralNetworkEnsembleModel.cs	(revision 16240)
@@ -36,15 +36,6 @@
   public sealed class NeuralNetworkEnsembleModel : ClassificationModel, INeuralNetworkEnsembleModel {
 
+    private object mlpEnsembleLocker = new object();
     private alglib.mlpensemble mlpEnsemble;
-    public alglib.mlpensemble MultiLayerPerceptronEnsemble {
-      get { return mlpEnsemble; }
-      set {
-        if (value != mlpEnsemble) {
-          if (value == null) throw new ArgumentNullException();
-          mlpEnsemble = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
 
     public override IEnumerable<string> VariablesUsedForPrediction {
@@ -103,5 +94,5 @@
         }
         // mlpeprocess writes data in mlpEnsemble and is therefore not thread-safe
-        lock (mlpEnsemble) {
+        lock (mlpEnsembleLocker) {
           alglib.mlpeprocess(mlpEnsemble, x, ref y);
         }
@@ -123,5 +114,5 @@
         }
         // mlpeprocess writes data in mlpEnsemble and is therefore not thread-safe
-        lock (mlpEnsemble) {
+        lock (mlpEnsembleLocker) {
           alglib.mlpeprocess(mlpEnsemble, x, ref y);
         }
@@ -145,14 +136,5 @@
       return new NeuralNetworkEnsembleClassificationSolution(this, new ClassificationEnsembleProblemData(problemData));
     }
-
-    #region events
-    public event EventHandler Changed;
-    private void OnChanged(EventArgs e) {
-      var handlers = Changed;
-      if (handlers != null)
-        handlers(this, e);
-    }
-    #endregion
-
+   
     #region persistence
     [Storable]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NeuralNetwork/NeuralNetworkModel.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NeuralNetwork/NeuralNetworkModel.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NeuralNetwork/NeuralNetworkModel.cs	(revision 16240)
@@ -36,15 +36,6 @@
   public sealed class NeuralNetworkModel : ClassificationModel, INeuralNetworkModel {
 
+    private object mlpLocker = new object();
     private alglib.multilayerperceptron multiLayerPerceptron;
-    public alglib.multilayerperceptron MultiLayerPerceptron {
-      get { return multiLayerPerceptron; }
-      set {
-        if (value != multiLayerPerceptron) {
-          if (value == null) throw new ArgumentNullException();
-          multiLayerPerceptron = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
 
     public override IEnumerable<string> VariablesUsedForPrediction {
@@ -107,5 +98,5 @@
         }
         // NOTE: mlpprocess changes data in multiLayerPerceptron and is therefore not thread-save!
-        lock (multiLayerPerceptron) {
+        lock (mlpLocker) {
           alglib.mlpprocess(multiLayerPerceptron, x, ref y);
         }
@@ -127,5 +118,5 @@
         }
         // NOTE: mlpprocess changes data in multiLayerPerceptron and is therefore not thread-save!
-        lock (multiLayerPerceptron) {
+        lock (mlpLocker) {
           alglib.mlpprocess(multiLayerPerceptron, x, ref y);
         }
@@ -150,13 +141,4 @@
     }
 
-    #region events
-    public event EventHandler Changed;
-    private void OnChanged(EventArgs e) {
-      var handlers = Changed;
-      if (handlers != null)
-        handlers(this, e);
-    }
-    #endregion
-
     #region persistence
     [Storable]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NonlinearRegression/NonlinearRegression.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NonlinearRegression/NonlinearRegression.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/NonlinearRegression/NonlinearRegression.cs	(revision 16240)
@@ -186,5 +186,5 @@
         qualityTable.Rows.Add(testRMSERow);
         Results.Add(new Result(qualityTable.Name, qualityTable.Name + " for all restarts", qualityTable));
-        if (SetSeedRandomly) Seed = (new System.Random()).Next();
+        if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
         var rand = new MersenneTwister((uint)Seed);
         bestSolution = CreateRegressionSolution(Problem.ProblemData, ModelStructure, Iterations, ApplyLinearScaling, rand);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestClassification.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestClassification.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestClassification.cs	(revision 16240)
@@ -135,5 +135,5 @@
     protected override void Run(CancellationToken cancellationToken) {
       double rmsError, relClassificationError, outOfBagRmsError, outOfBagRelClassificationError;
-      if (SetSeedRandomly) Seed = new System.Random().Next();
+      if (SetSeedRandomly) Seed = Random.RandomSeedGenerator.GetSeed();
 
       var model = CreateRandomForestClassificationModel(Problem.ProblemData, NumberOfTrees, R, M, Seed, out rmsError, out relClassificationError, out outOfBagRmsError, out outOfBagRelClassificationError);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestRegression.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestRegression.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestRegression.cs	(revision 16240)
@@ -134,5 +134,5 @@
     protected override void Run(CancellationToken cancellationToken) {
       double rmsError, avgRelError, outOfBagRmsError, outOfBagAvgRelError;
-      if (SetSeedRandomly) Seed = new System.Random().Next();
+      if (SetSeedRandomly) Seed = Random.RandomSeedGenerator.GetSeed();
       var model = CreateRandomForestRegressionModel(Problem.ProblemData, NumberOfTrees, R, M, Seed,
         out rmsError, out avgRelError, out outOfBagRmsError, out outOfBagAvgRelError);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs	(revision 16240)
@@ -275,5 +275,5 @@
       if (wdist != null) wdist.Initialize(problemData);
       if (state == null) {
-        if (SetSeedRandomly) Seed = new System.Random().Next();
+        if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
         var random = new MersenneTwister((uint)Seed);
         var dataset = problemData.Dataset;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.MOCMAEvolutionStrategy/3.3/MOCMAEvolutionStrategy.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.MOCMAEvolutionStrategy/3.3/MOCMAEvolutionStrategy.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.MOCMAEvolutionStrategy/3.3/MOCMAEvolutionStrategy.cs	(revision 16240)
@@ -285,5 +285,5 @@
     #region Initialization
     protected override void Initialize(CancellationToken cancellationToken) {
-      if (SetSeedRandomly) Seed = new System.Random().Next();
+      if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
       random.Reset(Seed);
       gauss = new NormalDistributedRandom(random, 0, 1);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.NSGA2/3.3/NSGA2.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.NSGA2/3.3/NSGA2.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.NSGA2/3.3/NSGA2.cs	(revision 16240)
@@ -166,5 +166,5 @@
       : base(original, cloner) {
       paretoFrontAnalyzer = (RankBasedParetoFrontAnalyzer)cloner.Clone(original.paretoFrontAnalyzer);
-      AfterDeserialization();
+      RegisterEventhandlers();
     }
     public NSGA2() {
@@ -240,5 +240,5 @@
       UpdateAnalyzers();
 
-      AfterDeserialization();
+      RegisterEventhandlers();
     }
 
@@ -338,4 +338,8 @@
       #endregion
 
+      RegisterEventhandlers();
+    }
+
+    private void RegisterEventhandlers() {
       PopulationSizeParameter.ValueChanged += new EventHandler(PopulationSizeParameter_ValueChanged);
       PopulationSize.ValueChanged += new EventHandler(PopulationSize_ValueChanged);
@@ -346,4 +350,5 @@
       }
     }
+
     private void ParameterizeSolutionsCreator() {
       SolutionsCreator.EvaluatorParameter.ActualName = Problem.EvaluatorParameter.Name;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.ParameterlessPopulationPyramid/3.3/ParameterlessPopulationPyramid.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.ParameterlessPopulationPyramid/3.3/ParameterlessPopulationPyramid.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Algorithms.ParameterlessPopulationPyramid/3.3/ParameterlessPopulationPyramid.cs	(revision 16240)
@@ -231,5 +231,5 @@
     protected override void Initialize(CancellationToken cancellationToken) {
       // Set up the algorithm
-      if (SetSeedRandomly) Seed = new System.Random().Next();
+      if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
       pyramid = new List<Population>();
       seen.Clear();
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Analysis/3.3/DataVisualization/IndexedDataRow.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Analysis/3.3/DataVisualization/IndexedDataRow.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Analysis/3.3/DataVisualization/IndexedDataRow.cs	(revision 16240)
@@ -20,12 +20,12 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
 using HeuristicLab.Collections;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
 
 namespace HeuristicLab.Analysis {
@@ -58,8 +58,23 @@
       set { visualProperties = value; }
     }
-    [Storable(Name = "values")]
+    // BackwardsCompatibility3.3
+    #region Backwards compatible code, remove with 3.4
+    // tuples are stored inefficiently
+    [Storable(Name = "values", AllowOneWay = true)]
     private IEnumerable<Tuple<T, double>> StorableValues {
-      get { return values; }
       set { values = new ObservableList<Tuple<T, double>>(value); }
+    }
+    #endregion
+    private T[] storableX;
+    [Storable(Name = "x")]
+    private T[] StorableX {
+      get { return Values.Select(x => x.Item1).ToArray(); }
+      set { storableX = value; }
+    }
+    private double[] storableY;
+    [Storable(Name = "y")]
+    private double[] StorableY {
+      get { return Values.Select(x => x.Item2).ToArray(); }
+      set { storableY = value; }
     }
     #endregion
@@ -109,4 +124,13 @@
       VisualProperties.DisplayName = Name;
     }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (storableX != null && storableY != null) {
+        values = new ObservableList<Tuple<T, double>>(storableX.Zip(storableY, (x, y) => Tuple.Create(x, y)));
+        storableX = null;
+        storableY = null;
+      } else if (values == null) throw new InvalidOperationException("Deserialization problem with IndexedDataRow.");
+    }
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableRoleListView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableRoleListView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableRoleListView.Designer.cs	(revision 16240)
@@ -66,5 +66,5 @@
       // 
       this.storeButton.Enabled = false;
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(33, 3);
       this.storeButton.Name = "storeButton";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableRoleSelectionListView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableRoleSelectionListView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableRoleSelectionListView.Designer.cs	(revision 16240)
@@ -53,5 +53,5 @@
       // 
       this.storeButton.Enabled = false;
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(33, 3);
       this.storeButton.Name = "storeButton";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableUserGroupListView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableUserGroupListView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableUserGroupListView.Designer.cs	(revision 16240)
@@ -66,5 +66,5 @@
       // 
       this.storeButton.Enabled = false;
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(33, 3);
       this.storeButton.Name = "storeButton";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableUserListView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableUserListView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RefreshableUserListView.Designer.cs	(revision 16240)
@@ -66,5 +66,5 @@
       this.storeButton.Enabled = false;
       this.storeButton.Location = new System.Drawing.Point(33, 3);
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Name = "storeButton";
       this.storeButton.Size = new System.Drawing.Size(24, 24);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RoleListView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RoleListView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/RoleListView.cs	(revision 16240)
@@ -29,5 +29,5 @@
   [View("RoleList View")]
   [Content(typeof(IItemList<Role>), true)]
-  public partial class RoleListView : ItemListView<Role> {
+  public partial class RoleListView : ItemCollectionView<Role> {
     public RoleListView() {
       InitializeComponent();
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupListView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupListView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupListView.cs	(revision 16240)
@@ -29,5 +29,5 @@
   [View("UserGroupList View")]
   [Content(typeof(IItemList<UserGroup>), true)]
-  public partial class UserGroupListView : ItemListView<UserGroup> {
+  public partial class UserGroupListView : ItemCollectionView<UserGroup> {
     public UserGroupListView() {
       InitializeComponent();
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.Designer.cs	(revision 16240)
@@ -49,5 +49,5 @@
       this.groupNameTextBox = new System.Windows.Forms.TextBox();
       this.idTextBox = new System.Windows.Forms.TextBox();
-      this.refreshableLightweightUserView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
+      this.refreshableLightweightUserView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightAccessClientView();
       this.storeButton = new System.Windows.Forms.Button();
       this.SuspendLayout();
@@ -85,5 +85,5 @@
       this.idTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                   | System.Windows.Forms.AnchorStyles.Right)));
-      this.idTextBox.Enabled = false;
+      this.idTextBox.ReadOnly = true;
       this.idTextBox.Location = new System.Drawing.Point(79, 29);
       this.idTextBox.Name = "idTextBox";
@@ -108,5 +108,5 @@
       // 
       this.storeButton.Enabled = false;
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(31, 58);
       this.storeButton.Name = "storeButton";
@@ -139,5 +139,5 @@
     private System.Windows.Forms.TextBox groupNameTextBox;
     private System.Windows.Forms.TextBox idTextBox;
-    private Views.RefreshableLightweightUserView refreshableLightweightUserView;
+    private Views.RefreshableLightweightAccessClientView refreshableLightweightUserView;
     private System.Windows.Forms.Button storeButton;
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.cs	(revision 16240)
@@ -40,10 +40,10 @@
     protected override void RegisterContentEvents() {
       base.RegisterContentEvents();
-      Access.AccessClient.Instance.Refreshing += new EventHandler(Content_Refreshing);
+      AccessClient.Instance.Refreshing += new EventHandler(Content_Refreshing);
       refreshableLightweightUserView.StorableStateChanged += new EventHandler(refreshableLightweightUserView_StorableStateChanged);
     }
 
     protected override void DeregisterContentEvents() {
-      Access.AccessClient.Instance.Refreshing -= new EventHandler(Content_Refreshing);
+      AccessClient.Instance.Refreshing -= new EventHandler(Content_Refreshing);
       refreshableLightweightUserView.StorableStateChanged -= new EventHandler(refreshableLightweightUserView_StorableStateChanged);
       base.DeregisterContentEvents();
@@ -63,12 +63,23 @@
         idTextBox.Text = Content.Id.ToString();
 
-        refreshableLightweightUserView.Content = Content.Id != Guid.Empty ? Access.AccessClient.Instance : null;
-        refreshableLightweightUserView.FetchSelectedUsers = Content.Id != Guid.Empty ? new Func<List<Guid>>(delegate { return AccessAdministrationClient.CallAccessService<List<Guid>>(s => s.GetUserGroupIdsOfGroup(Content.Id)); }) : null;
+        refreshableLightweightUserView.Content = Content.Id != Guid.Empty ? AccessClient.Instance : null;
+        refreshableLightweightUserView.FetchSelectedUsers = Content.Id != Guid.Empty ? new Func<List<Guid>>(delegate { return AccessAdministrationClient.CallAccessService(s => s.GetUserGroupIdsOfGroup(Content.Id)); }) : null;
       }
     }
 
-    private void groupNameTextBox_TextChanged(object sender, System.EventArgs e) {
-      if (Content.Name != this.groupNameTextBox.Text)
-        Content.Name = this.groupNameTextBox.Text;
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      groupNameTextBox.ReadOnly = !enabled;
+      idTextBox.ReadOnly = !enabled;
+      storeButton.Enabled = enabled;
+      //refreshableLightweightUserView.Enabled = enabled;
+      refreshableLightweightUserView.Locked = !enabled;
+      //refreshableLightweightUserView.ReadOnly = !enabled;
+    }
+
+    private void groupNameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != groupNameTextBox.Text)
+        Content.Name = groupNameTextBox.Text;
     }
 
@@ -91,9 +102,9 @@
 
     private void Content_Refreshing(object sender, EventArgs e) {
-      storeButton.Enabled = false;
+      if (!Locked) storeButton.Enabled = false;
     }
 
     private void refreshableLightweightUserView_StorableStateChanged(object sender, EventArgs e) {
-      storeButton.Enabled = true;
+      if (!Locked) storeButton.Enabled = true;
     }
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserListView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserListView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserListView.cs	(revision 16240)
@@ -29,5 +29,5 @@
   [View("UserList View")]
   [Content(typeof(IItemList<User>), true)]
-  public partial class UserListView : ItemListView<User> {
+  public partial class UserListView : ItemCollectionView<User> {
     public UserListView() {
       InitializeComponent();
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Administration/3.3/Views/UserView.cs	(revision 16240)
@@ -64,4 +64,10 @@
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      
+      userNameTextBox.ReadOnly = !enabled;
+      fullNameTextBox.ReadOnly = !enabled;
+      emailTextBox.ReadOnly = !enabled;
+
       if (Content == null) {
         resetPasswordButton.Enabled = false;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/HeuristicLab.Clients.Access.Views-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/HeuristicLab.Clients.Access.Views-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/HeuristicLab.Clients.Access.Views-3.3.csproj	(revision 16240)
@@ -147,4 +147,22 @@
       <DependentUpon>LightweightUserInformationView.cs</DependentUpon>
     </Compile>
+    <Compile Include="UserViews\RefreshableLightweightAccessClientInformationView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="UserViews\RefreshableLightweightAccessClientInformationView.Designer.cs">
+      <DependentUpon>RefreshableLightweightAccessClientInformationView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="UserViews\LightweightUserView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="UserViews\LightweightUserView.Designer.cs">
+      <DependentUpon>LightweightUserView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="UserViews\RefreshableLightweightAccessClientView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="UserViews\RefreshableLightweightAccessClientView.Designer.cs">
+      <DependentUpon>RefreshableLightweightAccessClientView.cs</DependentUpon>
+    </Compile>
     <Compile Include="UserViews\RefreshableLightweightUserInformationView.cs">
       <SubType>UserControl</SubType>
@@ -152,16 +170,4 @@
     <Compile Include="UserViews\RefreshableLightweightUserInformationView.Designer.cs">
       <DependentUpon>RefreshableLightweightUserInformationView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="UserViews\LightweightUserView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="UserViews\LightweightUserView.Designer.cs">
-      <DependentUpon>LightweightUserView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="UserViews\RefreshableLightweightUserView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="UserViews\RefreshableLightweightUserView.Designer.cs">
-      <DependentUpon>RefreshableLightweightUserView.cs</DependentUpon>
     </Compile>
     <Compile Include="UserViews\RefreshableView.cs">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/LightweightUserInformationView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/LightweightUserInformationView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/LightweightUserInformationView.cs	(revision 16240)
@@ -65,4 +65,15 @@
     }
 
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      //userNameTextBox.ReadOnly = !enabled;
+      fullNameTextBox.ReadOnly = !enabled;
+      emailTextBox.ReadOnly = !enabled;
+      //rolesListView.Enabled = enabled;
+      //groupsListView.Enabled = enabled;
+      changePasswordButton.Enabled = enabled;
+    }
+
     private void changePasswordButton_Click(object sender, System.EventArgs e) {
       using (ChangePasswordDialog dialog = new ChangePasswordDialog()) {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/LightweightUserView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/LightweightUserView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/LightweightUserView.cs	(revision 16240)
@@ -59,4 +59,9 @@
       base.SetEnabledStateOfControls();
       this.showDetailsCheckBox.Enabled = false;
+      bool enabled = Content != null && !Locked;
+      addButton.Enabled = enabled;
+      removeButton.Enabled = enabled;
+      moveDownButton.Enabled = enabled;
+      moveUpButton.Enabled = enabled;
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientInformationView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientInformationView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientInformationView.Designer.cs	(revision 16240)
@@ -0,0 +1,103 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+namespace HeuristicLab.Clients.Access.Views {
+  partial class RefreshableLightweightAccessClientInformationView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        lightweightUserInformationView.UserInformationChanged -= new EventHandler(lightweightUserInformationView_Changed);
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RefreshableLightweightAccessClientInformationView));
+      this.lightweightUserInformationView = new HeuristicLab.Clients.Access.Views.LightweightUserInformationView();
+      this.storeButton = new System.Windows.Forms.Button();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Location = new System.Drawing.Point(4, 3);
+      // 
+      // lightweightUserInformationView
+      // 
+      this.lightweightUserInformationView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.lightweightUserInformationView.Caption = "RefreshableLightweightUserInformation View";
+      this.lightweightUserInformationView.Content = null;
+      this.lightweightUserInformationView.Location = new System.Drawing.Point(3, 33);
+      this.lightweightUserInformationView.Name = "lightweightUserInformationView";
+      this.lightweightUserInformationView.ReadOnly = false;
+      this.lightweightUserInformationView.Size = new System.Drawing.Size(378, 245);
+      this.lightweightUserInformationView.TabIndex = 2;
+      // 
+      // storeButton
+      // 
+      this.storeButton.Enabled = false;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.storeButton.Location = new System.Drawing.Point(34, 3);
+      this.storeButton.Name = "storeButton";
+      this.storeButton.Size = new System.Drawing.Size(24, 24);
+      this.storeButton.TabIndex = 3;
+      this.storeButton.UseVisualStyleBackColor = true;
+      this.storeButton.Click += new System.EventHandler(this.storeButton_Click);
+      // 
+      // RefreshableLightweightAccessClientInformationView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.lightweightUserInformationView);
+      this.Controls.Add(this.storeButton);
+      this.Name = "RefreshableLightweightAccessClientInformationView";
+      this.Size = new System.Drawing.Size(384, 281);
+      this.Controls.SetChildIndex(this.storeButton, 0);
+      this.Controls.SetChildIndex(this.lightweightUserInformationView, 0);
+      this.Controls.SetChildIndex(this.refreshButton, 0);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private LightweightUserInformationView lightweightUserInformationView;
+    private System.Windows.Forms.Button storeButton;
+
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientInformationView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientInformationView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientInformationView.cs	(revision 16240)
@@ -0,0 +1,76 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Windows.Forms;
+
+namespace HeuristicLab.Clients.Access.Views {
+  public partial class RefreshableLightweightAccessClientInformationView : RefreshableView {
+    public RefreshableLightweightAccessClientInformationView() {
+      InitializeComponent();
+    }
+
+    private void RefreshUserData() {
+      UserInformation.Instance.Refresh();
+      lightweightUserInformationView.UserInformationChanged += new EventHandler(lightweightUserInformationView_Changed);
+    }
+
+    void lightweightUserInformationView_Changed(object sender, EventArgs e) {
+      if (!storeButton.Enabled) storeButton.Enabled = true;
+    }
+
+    protected override void RefreshData() {
+      Content.ExecuteActionAsync(RefreshUserData, PluginInfrastructure.ErrorHandling.ShowErrorDialog);
+    }
+
+    protected override void Content_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) {
+        Invoke(new EventHandler(Content_Refreshing), sender, e);
+      } else {
+        base.Content_Refreshing(sender, e);
+        lightweightUserInformationView.Enabled = false;
+        storeButton.Enabled = false;
+      }
+    }
+
+    protected override void Content_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) {
+        Invoke(new EventHandler(Content_Refreshed), sender, e);
+      } else {
+        base.Content_Refreshed(sender, e);
+        lightweightUserInformationView.Enabled = true;
+        if (!UserInformation.Instance.UserExists) {
+          MessageBox.Show("Couldn't fetch user information from the server." + Environment.NewLine +
+            "Please verify that you have an existing user and that your user name and password is correct. ", "HeuristicLab Access Service", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          lightweightUserInformationView.Content = null;
+        } else {
+          lightweightUserInformationView.Content = UserInformation.Instance.User;
+        }
+      }
+    }
+
+    private void storeButton_Click(object sender, EventArgs e) {
+      AccessClient.Instance.ExecuteActionAsync(new Action(delegate {
+        AccessClient.CallAccessService(x => x.UpdateLightweightUser(UserInformation.Instance.User));
+      }), HeuristicLab.PluginInfrastructure.ErrorHandling.ShowErrorDialog);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientView.Designer.cs	(revision 16240)
@@ -0,0 +1,80 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+namespace HeuristicLab.Clients.Access.Views {
+  partial class RefreshableLightweightAccessClientView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.lightweightUserView = new HeuristicLab.Clients.Access.Views.LightweightUserView();
+      this.SuspendLayout();
+      // 
+      // lightweightUserView
+      // 
+      this.lightweightUserView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.lightweightUserView.Caption = "LightweightUser View";
+      this.lightweightUserView.Content = null;
+      this.lightweightUserView.Location = new System.Drawing.Point(3, 33);
+      this.lightweightUserView.Name = "lightweightUserView";
+      this.lightweightUserView.ReadOnly = false;
+      this.lightweightUserView.Size = new System.Drawing.Size(500, 218);
+      this.lightweightUserView.TabIndex = 2;
+      this.lightweightUserView.SelectedUsersChanged += new System.EventHandler(this.lightweightUserView_SelectedUsersChanged);
+      // 
+      // RefreshableLightweightUserView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.lightweightUserView);
+      this.Name = "RefreshableLightweightUserView";
+      this.Controls.SetChildIndex(this.lightweightUserView, 0);
+      this.Controls.SetChildIndex(this.refreshButton, 0);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private LightweightUserView lightweightUserView;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightAccessClientView.cs	(revision 16240)
@@ -0,0 +1,133 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Core;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Clients.Access.Views {
+  [View("RefreshableLightweightAccessClient View")]
+  [Content(typeof(AccessClient), false)]
+  public partial class RefreshableLightweightAccessClientView : RefreshableView {
+    public RefreshableLightweightAccessClientView() {
+      InitializeComponent();
+    }
+
+    //set an action like a webservice call to retrieve the users which should be marked as checked           
+    private Func<List<Guid>> fetchSelectedUsers;
+    public Func<List<Guid>> FetchSelectedUsers {
+      get { return fetchSelectedUsers; }
+      set {
+        fetchSelectedUsers = value;
+        selectedUsers = null;
+        lightweightUserView.Content = null;
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private List<Guid> selectedUsers;
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+
+      selectedUsers = null;
+      lightweightUserView.Content = null;
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      refreshButton.Enabled = FetchSelectedUsers != null && Content != null;
+      lightweightUserView.Locked = Locked;
+    }
+
+    protected override void RefreshData() {
+      Action completeRefreshAction = new Action(delegate {
+        selectedUsers = FetchSelectedUsers();
+        Content.Refresh();
+      });
+
+      Content.ExecuteActionAsync(completeRefreshAction, new Action<Exception>((Exception ex) => ErrorHandling.ShowErrorDialog(this, "Refresh failed.", ex)));
+    }
+
+    protected override void Content_Refreshing(object sender, EventArgs e) {
+      base.Content_Refreshing(sender, e);
+      lightweightUserView.Content = null;
+    }
+
+    protected override void Content_Refreshed(object sender, EventArgs e) {
+      base.Content_Refreshed(sender, e);
+
+      if (Content.UsersAndGroups != null) {
+        lightweightUserView.Content = new ItemList<UserGroupBase>(Content.UsersAndGroups.Where(x => selectedUsers.Contains(x.Id)));
+        if (lightweightUserView.Content != null) OnStorableStateChanged();
+      }
+    }
+
+    public IItemList<UserGroupBase> GetDeletedUsers() {
+      if (lightweightUserView.Content == null) {
+        return null;
+      } else {
+        var deletedIds = selectedUsers.Where(x => lightweightUserView.Content.Count(y => y.Id == x) == 0);
+        List<UserGroupBase> deletedUGs = Content.UsersAndGroups.Where(x => deletedIds.Contains(x.Id)).ToList();
+        return new ItemList<UserGroupBase>(deletedUGs);
+      }
+    }
+
+    public IItemList<UserGroupBase> GetCheckedUsers() {
+      return (lightweightUserView.Content == null) ? null : lightweightUserView.Content;
+    }
+
+    public IItemList<UserGroupBase> GetAddedUsers() {
+      return (lightweightUserView.Content == null) ? null : new ItemList<UserGroupBase>(lightweightUserView.Content.Where(x => !selectedUsers.Contains(x.Id)));
+    }
+
+    public event EventHandler SelectedUsersChanged;
+    protected virtual void OnSelectedUsersChanged() {
+      if (InvokeRequired)
+        Invoke((MethodInvoker)OnSelectedUsersChanged);
+      else {
+        EventHandler handler = SelectedUsersChanged;
+        if (handler != null)
+          handler(this, EventArgs.Empty);
+      }
+    }
+
+    public event EventHandler StorableStateChanged;
+    protected virtual void OnStorableStateChanged() {
+      if (InvokeRequired)
+        Invoke((MethodInvoker)OnStorableStateChanged);
+      else {
+        EventHandler handler = StorableStateChanged;
+        if (handler != null)
+          handler(this, EventArgs.Empty);
+      }
+    }
+
+    private void lightweightUserView_SelectedUsersChanged(object sender, EventArgs e) {
+      OnSelectedUsersChanged();
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserInformationView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserInformationView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserInformationView.Designer.cs	(revision 16240)
@@ -20,5 +20,4 @@
 #endregion
 
-using System;
 namespace HeuristicLab.Clients.Access.Views {
   partial class RefreshableLightweightUserInformationView {
@@ -34,5 +33,4 @@
     protected override void Dispose(bool disposing) {
       if (disposing && (components != null)) {
-        lightweightUserInformationView.UserInformationChanged -= new EventHandler(lightweightUserInformationView_Changed);
         components.Dispose();
       }
@@ -47,12 +45,21 @@
     /// </summary>
     private void InitializeComponent() {
+      components = new System.ComponentModel.Container();
       System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RefreshableLightweightUserInformationView));
       this.lightweightUserInformationView = new HeuristicLab.Clients.Access.Views.LightweightUserInformationView();
-      this.storeButton = new System.Windows.Forms.Button();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
       this.SuspendLayout();
       // 
       // refreshButton
       // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
       this.refreshButton.Location = new System.Drawing.Point(4, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
       // 
       // lightweightUserInformationView
@@ -69,15 +76,4 @@
       this.lightweightUserInformationView.TabIndex = 2;
       // 
-      // storeButton
-      // 
-      this.storeButton.Enabled = false;
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
-      this.storeButton.Location = new System.Drawing.Point(34, 3);
-      this.storeButton.Name = "storeButton";
-      this.storeButton.Size = new System.Drawing.Size(24, 24);
-      this.storeButton.TabIndex = 3;
-      this.storeButton.UseVisualStyleBackColor = true;
-      this.storeButton.Click += new System.EventHandler(this.storeButton_Click);
-      // 
       // RefreshableLightweightUserInformationView
       // 
@@ -85,10 +81,9 @@
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
       this.Controls.Add(this.lightweightUserInformationView);
-      this.Controls.Add(this.storeButton);
+      this.Controls.Add(this.refreshButton);
       this.Name = "RefreshableLightweightUserInformationView";
       this.Size = new System.Drawing.Size(384, 281);
-      this.Controls.SetChildIndex(this.storeButton, 0);
       this.Controls.SetChildIndex(this.lightweightUserInformationView, 0);
-      this.Controls.SetChildIndex(this.refreshButton, 0);
+      this.Controls.SetChildIndex(this.refreshButton, 1);
       this.ResumeLayout(false);
 
@@ -98,6 +93,6 @@
 
     private LightweightUserInformationView lightweightUserInformationView;
-    private System.Windows.Forms.Button storeButton;
-
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.Button refreshButton;
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserInformationView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserInformationView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserInformationView.cs	(revision 16240)
@@ -22,9 +22,74 @@
 using System;
 using System.Windows.Forms;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
 
 namespace HeuristicLab.Clients.Access.Views {
-  public partial class RefreshableLightweightUserInformationView : RefreshableView {
+  [View("RefreshableLightweightUserInformation View")]
+  [Content(typeof(LightweightUser), false)]
+  public partial class RefreshableLightweightUserInformationView : AsynchronousContentView {
+    public new LightweightUser Content {
+      get { return (LightweightUser)base.Content; }
+      set { base.Content = value; }
+    }
+
     public RefreshableLightweightUserInformationView() {
       InitializeComponent();
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      lightweightUserInformationView.Content = Content;
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      this.Locked = true;
+      refreshButton.Enabled = Content != null;
+    }
+
+    protected override void DeregisterContentEvents() {
+      Refreshing -= new EventHandler(Content_Refreshing);
+      Refreshed -= new EventHandler(Content_Refreshed);
+      base.DeregisterContentEvents();
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Refreshing += new EventHandler(Content_Refreshing);
+      Refreshed += new EventHandler(Content_Refreshed);
+    }
+
+    protected void Content_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) {
+        Invoke(new EventHandler(Content_Refreshing), sender, e);
+      } else {
+        Cursor = Cursors.AppStarting;
+        refreshButton.Enabled = false;
+
+        lightweightUserInformationView.Enabled = false;
+      }
+    }
+
+    protected void Content_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) {
+        Invoke(new EventHandler(Content_Refreshed), sender, e);
+      } else {
+        Cursor = Cursors.Default;
+        refreshButton.Enabled = true;
+
+        lightweightUserInformationView.Enabled = true;
+        if (!UserInformation.Instance.UserExists) {
+          MessageBox.Show("Couldn't fetch user information from the server." + Environment.NewLine +
+            "Please verify that you have an existing user and that your user name and password is correct. ", "HeuristicLab Access Service", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          lightweightUserInformationView.Content = null;
+        } else {
+          lightweightUserInformationView.Content = Content;
+        }
+      }
+    }
+
+    void lightweightUserInformationView_Changed(object sender, EventArgs e) {
+      // nothing to do
     }
 
@@ -34,43 +99,46 @@
     }
 
-    void lightweightUserInformationView_Changed(object sender, EventArgs e) {
-      if (!storeButton.Enabled) storeButton.Enabled = true;
+    private void RefreshData() {
+      ExecuteActionAsync(RefreshUserData, PluginInfrastructure.ErrorHandling.ShowErrorDialog);
     }
 
-    protected override void RefreshData() {
-      Content.ExecuteActionAsync(RefreshUserData, PluginInfrastructure.ErrorHandling.ShowErrorDialog);
+    public void ManualRefresh() {
+      RefreshData();
     }
 
-    protected override void Content_Refreshing(object sender, EventArgs e) {
-      if (InvokeRequired) {
-        Invoke(new EventHandler(Content_Refreshing), sender, e);
-      } else {
-        base.Content_Refreshing(sender, e);
-        lightweightUserInformationView.Enabled = false;
-        storeButton.Enabled = false;
-      }
+    protected void refreshButton_Click(object sender, System.EventArgs e) {
+      RefreshData();
     }
 
-    protected override void Content_Refreshed(object sender, EventArgs e) {
-      if (InvokeRequired) {
-        Invoke(new EventHandler(Content_Refreshed), sender, e);
-      } else {
-        base.Content_Refreshed(sender, e);
-        lightweightUserInformationView.Enabled = true;
-        if (!UserInformation.Instance.UserExists) {
-          MessageBox.Show("Couldn't fetch user information from the server." + Environment.NewLine +
-            "Please verify that you have an existing user and that your user name and password is correct. ", "HeuristicLab Access Service", MessageBoxButtons.OK, MessageBoxIcon.Error);
-          lightweightUserInformationView.Content = null;
-        } else {
-          lightweightUserInformationView.Content = UserInformation.Instance.User;
+    public void ExecuteActionAsync(Action action, Action<Exception> exceptionCallback) {
+      var call = new Func<Exception>(delegate () {
+        try {
+          OnRefreshing();
+          action();
+        } catch (Exception ex) {
+          return ex;
+        } finally {
+          OnRefreshed();
         }
-      }
+        return null;
+      });
+      call.BeginInvoke(delegate (IAsyncResult result) {
+        Exception ex = call.EndInvoke(result);
+        if (ex != null) exceptionCallback(ex);
+      }, null);
     }
 
-    private void storeButton_Click(object sender, EventArgs e) {
-      AccessClient.Instance.ExecuteActionAsync(new Action(delegate {
-        AccessClient.CallAccessService(x => x.UpdateLightweightUser(UserInformation.Instance.User));
-      }), HeuristicLab.PluginInfrastructure.ErrorHandling.ShowErrorDialog);
+    #region Events
+    public event EventHandler Refreshing;
+    private void OnRefreshing() {
+      EventHandler handler = Refreshing;
+      if (handler != null) handler(this, EventArgs.Empty);
     }
+    public event EventHandler Refreshed;
+    private void OnRefreshed() {
+      EventHandler handler = Refreshed;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    #endregion
   }
 }
Index: anches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserView.Designer.cs	(revision 16239)
+++ 	(revision )
@@ -1,80 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-namespace HeuristicLab.Clients.Access.Views {
-  partial class RefreshableLightweightUserView {
-    /// <summary> 
-    /// Required designer variable.
-    /// </summary>
-    private System.ComponentModel.IContainer components = null;
-
-    /// <summary> 
-    /// Clean up any resources being used.
-    /// </summary>
-    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-    protected override void Dispose(bool disposing) {
-      if (disposing && (components != null)) {
-        components.Dispose();
-      }
-      base.Dispose(disposing);
-    }
-
-    #region Component Designer generated code
-
-    /// <summary> 
-    /// Required method for Designer support - do not modify 
-    /// the contents of this method with the code editor.
-    /// </summary>
-    private void InitializeComponent() {
-      this.lightweightUserView = new HeuristicLab.Clients.Access.Views.LightweightUserView();
-      this.SuspendLayout();
-      // 
-      // lightweightUserView
-      // 
-      this.lightweightUserView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                  | System.Windows.Forms.AnchorStyles.Left)
-                  | System.Windows.Forms.AnchorStyles.Right)));
-      this.lightweightUserView.Caption = "LightweightUser View";
-      this.lightweightUserView.Content = null;
-      this.lightweightUserView.Location = new System.Drawing.Point(3, 33);
-      this.lightweightUserView.Name = "lightweightUserView";
-      this.lightweightUserView.ReadOnly = false;
-      this.lightweightUserView.Size = new System.Drawing.Size(500, 218);
-      this.lightweightUserView.TabIndex = 2;
-      this.lightweightUserView.SelectedUsersChanged += new System.EventHandler(this.lightweightUserView_SelectedUsersChanged);
-      // 
-      // RefreshableLightweightUserView
-      // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-      this.Controls.Add(this.lightweightUserView);
-      this.Name = "RefreshableLightweightUserView";
-      this.Controls.SetChildIndex(this.lightweightUserView, 0);
-      this.Controls.SetChildIndex(this.refreshButton, 0);
-      this.ResumeLayout(false);
-
-    }
-
-    #endregion
-
-    private LightweightUserView lightweightUserView;
-  }
-}
Index: anches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableLightweightUserView.cs	(revision 16239)
+++ 	(revision )
@@ -1,132 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows.Forms;
-using HeuristicLab.Core;
-using HeuristicLab.MainForm;
-using HeuristicLab.MainForm.WindowsForms;
-using HeuristicLab.PluginInfrastructure;
-
-namespace HeuristicLab.Clients.Access.Views {
-  [View("RefreshableLightweightUser View")]
-  [Content(typeof(AccessClient), false)]
-  public partial class RefreshableLightweightUserView : RefreshableView {
-    public RefreshableLightweightUserView() {
-      InitializeComponent();
-    }
-
-    //set an action like a webservice call to retrieve the users which should be marked as checked           
-    private Func<List<Guid>> fetchSelectedUsers;
-    public Func<List<Guid>> FetchSelectedUsers {
-      get { return fetchSelectedUsers; }
-      set {
-        fetchSelectedUsers = value;
-        selectedUsers = null;
-        lightweightUserView.Content = null;
-        SetEnabledStateOfControls();
-      }
-    }
-
-    private List<Guid> selectedUsers;
-
-    protected override void OnContentChanged() {
-      base.OnContentChanged();
-
-      selectedUsers = null;
-      lightweightUserView.Content = null;
-    }
-
-    protected override void RefreshData() {
-      Action completeRefreshAction = new Action(delegate {
-        selectedUsers = FetchSelectedUsers();
-        Content.Refresh();
-      });
-
-      Content.ExecuteActionAsync(completeRefreshAction, new Action<Exception>((Exception ex) => ErrorHandling.ShowErrorDialog(this, "Refresh failed.", ex)));
-    }
-
-    protected override void Content_Refreshing(object sender, EventArgs e) {
-      base.Content_Refreshing(sender, e);
-      lightweightUserView.Content = null;
-    }
-
-    protected override void Content_Refreshed(object sender, EventArgs e) {
-      base.Content_Refreshed(sender, e);
-
-      if (Content.UsersAndGroups != null) {
-        lightweightUserView.Content = new ItemList<UserGroupBase>(Content.UsersAndGroups.Where(x => selectedUsers.Contains(x.Id)));
-        if (lightweightUserView.Content != null) OnStorableStateChanged();
-      }
-    }
-
-    protected override void SetEnabledStateOfControls() {
-      base.SetEnabledStateOfControls();
-      refreshButton.Enabled = FetchSelectedUsers != null && Content != null;
-    }
-
-    public IItemList<UserGroupBase> GetDeletedUsers() {
-      if (lightweightUserView.Content == null) {
-        return null;
-      } else {
-        var deletedIds = selectedUsers.Where(x => lightweightUserView.Content.Count(y => y.Id == x) == 0);
-        List<UserGroupBase> deletedUGs = Content.UsersAndGroups.Where(x => deletedIds.Contains(x.Id)).ToList();
-        return new ItemList<UserGroupBase>(deletedUGs);
-      }
-    }
-
-    public IItemList<UserGroupBase> GetCheckedUsers() {
-      return (lightweightUserView.Content == null) ? null : lightweightUserView.Content;
-    }
-
-    public IItemList<UserGroupBase> GetAddedUsers() {
-      return (lightweightUserView.Content == null) ? null : new ItemList<UserGroupBase>(lightweightUserView.Content.Where(x => !selectedUsers.Contains(x.Id)));
-    }
-
-    public event EventHandler SelectedUsersChanged;
-    protected virtual void OnSelectedUsersChanged() {
-      if (InvokeRequired)
-        Invoke((MethodInvoker)OnSelectedUsersChanged);
-      else {
-        EventHandler handler = SelectedUsersChanged;
-        if (handler != null)
-          handler(this, EventArgs.Empty);
-      }
-    }
-
-    public event EventHandler StorableStateChanged;
-    protected virtual void OnStorableStateChanged() {
-      if (InvokeRequired)
-        Invoke((MethodInvoker)OnStorableStateChanged);
-      else {
-        EventHandler handler = StorableStateChanged;
-        if (handler != null)
-          handler(this, EventArgs.Empty);
-      }
-    }
-
-    private void lightweightUserView_SelectedUsersChanged(object sender, EventArgs e) {
-      OnSelectedUsersChanged();
-    }
-  }
-}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/RefreshableView.Designer.cs	(revision 16240)
@@ -45,6 +45,8 @@
     /// </summary>
     private void InitializeComponent() {
+      components = new System.ComponentModel.Container();
       System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RefreshableView));
       this.refreshButton = new System.Windows.Forms.Button();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
       this.SuspendLayout();
       // 
@@ -56,4 +58,5 @@
       this.refreshButton.Size = new System.Drawing.Size(24, 24);
       this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
       this.refreshButton.UseVisualStyleBackColor = true;
       this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
@@ -72,4 +75,5 @@
     #endregion
 
+    private System.Windows.Forms.ToolTip toolTip;
     protected System.Windows.Forms.Button refreshButton;
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/UserInformationDialog.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/UserInformationDialog.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access.Views/3.3/UserViews/UserInformationDialog.Designer.cs	(revision 16240)
@@ -46,5 +46,5 @@
     private void InitializeComponent() {
       this.closeButton = new System.Windows.Forms.Button();
-      this.refreshableLightweightUserInformationView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserInformationView();
+      this.refreshableLightweightUserInformationView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightAccessClientInformationView();
       this.SuspendLayout();
       // 
@@ -95,5 +95,5 @@
     #endregion
 
-    private RefreshableLightweightUserInformationView refreshableLightweightUserInformationView;
+    private RefreshableLightweightAccessClientInformationView refreshableLightweightUserInformationView;
     private System.Windows.Forms.Button closeButton;
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/HeuristicLab.Clients.Access-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/HeuristicLab.Clients.Access-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/HeuristicLab.Clients.Access-3.3.csproj	(revision 16240)
@@ -106,4 +106,5 @@
     <Compile Include="ClientInformation.cs" />
     <Compile Include="ClientInformationUtils.cs" />
+    <Compile Include="HiveRoles.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/HiveRoles.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/HiveRoles.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/HiveRoles.cs	(revision 16240)
@@ -0,0 +1,38 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Linq;
+
+namespace HeuristicLab.Clients.Access {
+  public static class HiveRoles {
+    public const string Administrator = "Hive Administrator";
+    public const string User = "Hive User";
+    public const string Slave = "Hive Slave";
+
+    public static bool CheckAdminUserPermissions() {
+      return UserInformation.Instance.UserExists && UserInformation.Instance.User.Roles.Any(x => x.Name == Administrator);
+    }
+
+    public static bool CheckHiveUserPermissions() {
+      return UserInformation.Instance.UserExists && UserInformation.Instance.User.Roles.Any(x => x.Name == User);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/ServiceClients/User.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/ServiceClients/User.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Access/3.3/ServiceClients/User.cs	(revision 16240)
@@ -51,5 +51,5 @@
 
     public override string ToString() {
-      return UserName + "(" + FullName + ")";
+      return UserName + " (" + FullName + ")";
     }
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Common/3.3/HeuristicLab.Clients.Common-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Common/3.3/HeuristicLab.Clients.Common-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Common/3.3/HeuristicLab.Clients.Common-3.3.csproj	(revision 16240)
@@ -139,4 +139,5 @@
       <DependentUpon>Settings.settings</DependentUpon>
     </Compile>
+    <Compile Include="SecurityExceptionUtil.cs" />
     <None Include="app.config" />
     <None Include="Properties\AssemblyInfo.cs.frame" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Common/3.3/SecurityExceptionUtil.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Common/3.3/SecurityExceptionUtil.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Common/3.3/SecurityExceptionUtil.cs	(revision 16240)
@@ -0,0 +1,32 @@
+﻿using System;
+using System.ServiceModel.Security;
+using System.Threading.Tasks;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Clients.Hive {
+  public class SecurityExceptionUtil {
+    public static void TryAndReportSecurityExceptions(Action action, Action finallyCallback = null) {
+      try {
+        action();
+      } catch (MessageSecurityException ex) {
+        ErrorHandling.ShowErrorDialog("There is something wrong with the applied security. Are your credentials correct?", ex);
+      } catch (SecurityAccessDeniedException ex) {
+        ErrorHandling.ShowErrorDialog("The security authorization request failed. Are you a Hive administrator?", ex);
+      } finally {
+        if (finallyCallback != null) finallyCallback();
+      }
+    }
+
+    public static async Task TryAsyncAndReportSecurityExceptions(Action action, Action finallyCallback = null) {
+      try {
+        await Task.Run(action);
+      } catch (MessageSecurityException ex) {
+        ErrorHandling.ShowErrorDialog("There is something wrong with the applied security. Are your credentials correct?", ex);
+      } catch (SecurityAccessDeniedException ex) {
+        ErrorHandling.ShowErrorDialog("The security authorization request failed. Are you a Hive administrator?", ex);
+      } finally {
+        if (finallyCallback != null) finallyCallback();
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj	(revision 16240)
@@ -110,5 +110,4 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="HiveRoles.cs" />
     <Compile Include="Views\DowntimeTypeDialog.cs">
       <SubType>Form</SubType>
@@ -133,4 +132,34 @@
     </Compile>
     <Compile Include="MenuItems\AdministratorMenuItem.cs" />
+    <Compile Include="Views\ProjectJobsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectJobsView.Designer.cs">
+      <DependentUpon>ProjectJobsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectPermissionsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectPermissionsView.Designer.cs">
+      <DependentUpon>ProjectPermissionsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectResourcesView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectResourcesView.Designer.cs">
+      <DependentUpon>ProjectResourcesView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectsView.Designer.cs">
+      <DependentUpon>ProjectsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectView.Designer.cs">
+      <DependentUpon>ProjectView.cs</DependentUpon>
+    </Compile>
     <Compile Include="Views\Recurrence.cs">
       <SubType>Form</SubType>
@@ -151,4 +180,10 @@
     <Compile Include="Views\ScheduleView.Designer.cs">
       <DependentUpon>ScheduleView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ResourceView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ResourceView.Designer.cs">
+      <DependentUpon>ResourceView.cs</DependentUpon>
     </Compile>
     <Compile Include="Views\SlaveView.cs">
@@ -167,4 +202,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Clients.Access.Administration\3.3\HeuristicLab.Clients.Access.Administration-3.3.csproj">
+      <Project>{C9CF6AE8-4637-4A75-9141-042F479B5D50}</Project>
+      <Name>HeuristicLab.Clients.Access.Administration-3.3</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Clients.Access.Views\3.3\HeuristicLab.Clients.Access.Views-3.3.csproj">
       <Project>{FE856595-64CD-46DA-9CD2-FFF3E6B0D4F2}</Project>
@@ -214,4 +253,8 @@
       <Name>HeuristicLab.Core-3.3</Name>
       <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Data.Views\3.3\HeuristicLab.Data.Views-3.3.csproj">
+      <Project>{72104A0B-90E7-42F3-9ABE-9BBBADD4B943}</Project>
+      <Name>HeuristicLab.Data.Views-3.3</Name>
     </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Data\3.3\HeuristicLab.Data-3.3.csproj">
Index: anches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/HiveRoles.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/HiveRoles.cs	(revision 16239)
+++ 	(revision )
@@ -1,35 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System.Linq;
-using HeuristicLab.Clients.Access;
-
-namespace HeuristicLab.Clients.Hive.Administrator {
-  public static class HiveRoles {
-    public const string Administrator = "Hive Administrator";
-    public const string User = "Hive User";
-    public const string Slave = "Hive Slave";
-
-    public static bool CheckAdminUserPermissions() {
-      return UserInformation.Instance.UserExists && UserInformation.Instance.User.Roles.Any(x => x.Name == HiveRoles.Administrator);
-    }
-  }
-}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/MenuItems/AdministratorMenuItem.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/MenuItems/AdministratorMenuItem.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/MenuItems/AdministratorMenuItem.cs	(revision 16240)
@@ -20,21 +20,44 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
-using HeuristicLab.Clients.Hive;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
 using HeuristicLab.MainForm;
+using HeuristicLab.Optimizer;
 
-namespace HeuristicLab.Optimizer.MenuItems {
-  public class AdministratorMenuItem : HeuristicLab.MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
+namespace HeuristicLab.Clients.Hive.Administrator {
+  public class AdministratorMenuItem : MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
     public override string Name {
       get { return "&Administrator"; }
     }
+
     public override IEnumerable<string> Structure {
       get { return new string[] { "&Services", "&Hive" }; }
     }
+
     public override void Execute() {
-      MainFormManager.MainForm.ShowContent(HiveAdminClient.Instance);
+
+      if (HiveRoles.CheckAdminUserPermissions() || HiveAdminClient.Instance.CheckAccessToAdminAreaGranted()) {
+        MainFormManager.MainForm.ShowContent(HiveAdminClient.Instance);
+      } else if (!UserInformation.Instance.UserExists) {
+        MessageBox.Show(
+          "Couldn't fetch user information from the server." + Environment.NewLine +
+          "Please verify that you have an existing user and that your user name and password is correct.",
+          "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      } else {
+        MessageBox.Show(
+          "You do not seem to have the permissions to use the Hive Administrator." + Environment.NewLine +
+          "If that's not the case or you have any questions please write an email to support@heuristiclab.com",
+          "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      }
     }
+
     public override int Position {
-      get { return 10000; }
+      get { return 8000; }
+    }
+
+    public override Keys ShortCutKeys {
+      get { return Keys.Control | Keys.Shift | Keys.H; }
     }
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Plugin.cs.frame
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Plugin.cs.frame	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Plugin.cs.frame	(revision 16240)
@@ -32,6 +32,9 @@
   [PluginDependency("HeuristicLab.Core", "3.3")]
   [PluginDependency("HeuristicLab.Core.Views", "3.3")]  
+  [PluginDependency("HeuristicLab.Clients.Common", "3.3")]
   [PluginDependency("HeuristicLab.Clients.Hive", "3.3")]
   [PluginDependency("HeuristicLab.Clients.Hive.Views", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Data.Views", "3.3")]
   [PluginDependency("HeuristicLab.DayView", "1.0.0.0")]
   [PluginDependency("HeuristicLab.Optimizer", "3.3")]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/HiveAdministratorView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/HiveAdministratorView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/HiveAdministratorView.Designer.cs	(revision 16240)
@@ -29,17 +29,21 @@
       this.components = new System.ComponentModel.Container();
       this.tabAdmin = new System.Windows.Forms.TabControl();
-      this.tabSlaves = new System.Windows.Forms.TabPage();
+      this.tabResources = new System.Windows.Forms.TabPage();
+      this.resourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ResourcesView();
+      this.tabProjects = new System.Windows.Forms.TabPage();
       this.imageListUsers = new System.Windows.Forms.ImageList(this.components);
-      this.resourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ResourcesView();
+      this.projectsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectsView();
       this.tabAdmin.SuspendLayout();
-      this.tabSlaves.SuspendLayout();
+      this.tabResources.SuspendLayout();
+      this.tabProjects.SuspendLayout();
       this.SuspendLayout();
       // 
       // tabAdmin
       // 
-      this.tabAdmin.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                  | System.Windows.Forms.AnchorStyles.Left)
-                  | System.Windows.Forms.AnchorStyles.Right)));
-      this.tabAdmin.Controls.Add(this.tabSlaves);
+      this.tabAdmin.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.tabAdmin.Controls.Add(this.tabResources);
+      this.tabAdmin.Controls.Add(this.tabProjects);
       this.tabAdmin.Location = new System.Drawing.Point(3, 0);
       this.tabAdmin.Name = "tabAdmin";
@@ -48,26 +52,20 @@
       this.tabAdmin.TabIndex = 0;
       // 
-      // tabSlaves
+      // tabResources
       // 
-      this.tabSlaves.Controls.Add(this.resourcesView);
-      this.tabSlaves.Location = new System.Drawing.Point(4, 22);
-      this.tabSlaves.Name = "tabSlaves";
-      this.tabSlaves.Padding = new System.Windows.Forms.Padding(3);
-      this.tabSlaves.Size = new System.Drawing.Size(734, 520);
-      this.tabSlaves.TabIndex = 3;
-      this.tabSlaves.Text = "Slaves";
-      this.tabSlaves.UseVisualStyleBackColor = true;
-      // 
-      // imageListUsers
-      // 
-      this.imageListUsers.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
-      this.imageListUsers.ImageSize = new System.Drawing.Size(16, 16);
-      this.imageListUsers.TransparentColor = System.Drawing.Color.Transparent;
+      this.tabResources.Controls.Add(this.resourcesView);
+      this.tabResources.Location = new System.Drawing.Point(4, 22);
+      this.tabResources.Name = "tabResources";
+      this.tabResources.Padding = new System.Windows.Forms.Padding(3);
+      this.tabResources.Size = new System.Drawing.Size(734, 520);
+      this.tabResources.TabIndex = 3;
+      this.tabResources.Text = "Resources";
+      this.tabResources.UseVisualStyleBackColor = true;
       // 
       // resourcesView
       // 
-      this.resourcesView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                  | System.Windows.Forms.AnchorStyles.Left)
-                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.resourcesView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
       this.resourcesView.Caption = "ResourcesView";
       this.resourcesView.Content = null;
@@ -78,13 +76,42 @@
       this.resourcesView.TabIndex = 0;
       // 
-      // HiveAdministrationView
+      // tabProjects
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.tabProjects.Controls.Add(this.projectsView);
+      this.tabProjects.Location = new System.Drawing.Point(4, 22);
+      this.tabProjects.Name = "tabProjects";
+      this.tabProjects.Size = new System.Drawing.Size(734, 520);
+      this.tabProjects.TabIndex = 4;
+      this.tabProjects.Text = "Projects";
+      this.tabProjects.UseVisualStyleBackColor = true;
+      // 
+      // imageListUsers
+      // 
+      this.imageListUsers.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
+      this.imageListUsers.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageListUsers.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // projectsView
+      // 
+      this.projectsView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectsView.Caption = "Resources View";
+      this.projectsView.Content = null;
+      this.projectsView.Location = new System.Drawing.Point(0, 0);
+      this.projectsView.Name = "projectsView";
+      this.projectsView.ReadOnly = false;
+      this.projectsView.Size = new System.Drawing.Size(734, 520);
+      this.projectsView.TabIndex = 0;
+      // 
+      // HiveAdministratorView
+      // 
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.Controls.Add(this.tabAdmin);
-      this.Name = "HiveAdministrationView";
+      this.Name = "HiveAdministratorView";
       this.Size = new System.Drawing.Size(745, 546);
       this.tabAdmin.ResumeLayout(false);
-      this.tabSlaves.ResumeLayout(false);
+      this.tabResources.ResumeLayout(false);
+      this.tabProjects.ResumeLayout(false);
       this.ResumeLayout(false);
 
@@ -95,6 +122,7 @@
     private System.ComponentModel.IContainer components;
     private System.Windows.Forms.ImageList imageListUsers;
-    private System.Windows.Forms.TabPage tabSlaves;
-
+    private System.Windows.Forms.TabPage tabResources;
+    private System.Windows.Forms.TabPage tabProjects;
+    private ProjectsView projectsView;
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.Designer.cs	(revision 16240)
@@ -0,0 +1,161 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Windows.Forms;
+using HeuristicLab.Data.Views;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectJobsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.removeButton = new System.Windows.Forms.Button();
+      this.startButton = new System.Windows.Forms.Button();
+      this.stopButton = new System.Windows.Forms.Button();
+      this.pauseButton = new System.Windows.Forms.Button();
+      this.matrixView = new HeuristicLab.Data.Views.StringConvertibleMatrixView();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // removeButton
+      // 
+      this.removeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Remove;
+      this.removeButton.Location = new System.Drawing.Point(33, 3);
+      this.removeButton.Name = "removeButton";
+      this.removeButton.Size = new System.Drawing.Size(24, 24);
+      this.removeButton.TabIndex = 2;
+      this.toolTip.SetToolTip(this.removeButton, "Remove job(s)");
+      this.removeButton.UseVisualStyleBackColor = true;
+      this.removeButton.Click += new System.EventHandler(this.removeButton_Click);
+      // 
+      // startButton
+      // 
+      this.startButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
+      this.startButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Play;
+      this.startButton.Location = new System.Drawing.Point(63, 3);
+      this.startButton.Name = "startButton";
+      this.startButton.Size = new System.Drawing.Size(24, 24);
+      this.startButton.TabIndex = 4;
+      this.toolTip.SetToolTip(this.startButton, "Resume job(s)");
+      this.startButton.UseVisualStyleBackColor = true;
+      this.startButton.Click += new System.EventHandler(this.startButton_Click);
+      // 
+      // pauseButton
+      // 
+      this.pauseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
+      this.pauseButton.Enabled = false;
+      this.pauseButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Pause;
+      this.pauseButton.Location = new System.Drawing.Point(93, 3);
+      this.pauseButton.Name = "pauseButton";
+      this.pauseButton.Size = new System.Drawing.Size(24, 24);
+      this.pauseButton.TabIndex = 5;
+      this.toolTip.SetToolTip(this.pauseButton, "Pause job(s)");
+      this.pauseButton.UseVisualStyleBackColor = true;
+      this.pauseButton.Click += new System.EventHandler(this.pauseButton_Click);
+      // 
+      // stopButton
+      // 
+      this.stopButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
+      this.stopButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Stop;
+      this.stopButton.Location = new System.Drawing.Point(123, 3);
+      this.stopButton.Name = "stopButton";
+      this.stopButton.Size = new System.Drawing.Size(24, 24);
+      this.stopButton.TabIndex = 6;
+      this.toolTip.SetToolTip(this.stopButton, "Stop job(s)");
+      this.stopButton.UseVisualStyleBackColor = true;
+      this.stopButton.Click += new System.EventHandler(this.stopButton_Click);
+      // 
+      // matrixView
+      // 
+      this.matrixView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+            | System.Windows.Forms.AnchorStyles.Left)
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.matrixView.Caption = "StringConvertibleMatrix View";
+      this.matrixView.Content = null;
+      this.matrixView.Location = new System.Drawing.Point(3, 33);
+      this.matrixView.Name = "matrixView";
+      this.matrixView.ReadOnly = false;
+      this.matrixView.ShowRowsAndColumnsTextBox = false;
+      this.matrixView.ShowStatisticalInformation = false;
+      this.matrixView.Size = new System.Drawing.Size(504, 349);
+      this.matrixView.TabIndex = 3;
+      // 
+      // ProjectJobsView
+      // 
+      this.AllowDrop = true;
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.matrixView);
+      this.Controls.Add(this.refreshButton);
+      this.Controls.Add(this.removeButton);
+      this.Controls.Add(this.startButton);
+      this.Controls.Add(this.pauseButton);
+      this.Controls.Add(this.stopButton);
+      this.Name = "ProjectJobsView";
+      this.Size = new System.Drawing.Size(510, 385);
+      this.Load += new System.EventHandler(this.ProjectJobsView_Load);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button removeButton;
+    private System.Windows.Forms.Button startButton;
+    private System.Windows.Forms.Button stopButton;
+    private System.Windows.Forms.Button pauseButton;
+    private StringConvertibleMatrixView matrixView;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs	(revision 16240)
@@ -0,0 +1,410 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
+using HeuristicLab.Core.Views;
+using HeuristicLab.Data;
+using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.Core;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectJobsView : ItemView {
+    private const string JOB_ID = "Id";
+    private const string JOB_NAME = "Name";
+    private const string JOB_OWNER = "Owner";
+    private const string JOB_OWNERID = "Owner Id";
+    private const string JOB_DATECREATED = "Date Created";
+    private const string JOB_STATE = "State";
+    private const string JOB_EXECUTIONSTATE = "Execution State";
+    private const string JOB_EXECUTIONTIME = "Execution Time";
+    private const string JOB_DESCRIPTION = "Description";
+    private const string JOB_TASKCOUNT = "Tasks";
+    private const string JOB_WAITINGTASKCOUNT = "Waiting";
+    private const string JOB_CALCULATINGTASKCOUNT = "Calculating";
+    private const string JOB_FINISHEDTASKCOUNT = "Finished";
+
+    private readonly Color onlineStatusColor = Color.FromArgb(255, 189, 249, 143); // #bdf98f
+    private readonly Color onlineStatusColor2 = Color.FromArgb(255, 157, 249, 143); // #9df98f
+    private readonly Color statisticsPendingStatusColor = Color.FromArgb(255, 249, 210, 145); // #f9d291
+    private readonly Color deletionPendingStatusColor = Color.FromArgb(255, 249, 172, 144); // #f9ac90
+    private readonly Color deletionPendingStatusColor2 = Color.FromArgb(255, 249, 149, 143); // #f9958f
+
+    private IProgress progress;
+    public IProgress Progress {
+      get { return progress; }
+      set {
+        this.progress = value;
+        OnIsProgressingChanged();
+      }
+    }
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectJobsView() {
+      InitializeComponent();
+      progress = new Progress();
+
+      removeButton.Enabled = false;      
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      matrixView.DataGridView.SelectionChanged += DataGridView_SelectionChanged;
+      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, progress);
+    }
+
+    protected override void DeregisterContentEvents() {
+      matrixView.DataGridView.SelectionChanged -= DataGridView_SelectionChanged;
+      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this, false);
+      base.DeregisterContentEvents();
+    }
+
+    private void DataGridView_SelectionChanged(object sender, EventArgs e) {
+      SetEnabledStateOfControls();
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      removeButton.Enabled = false;
+      UpdateJobs();
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+
+      matrixView.Enabled = enabled;
+
+      // Buttons (start/resume, pause, stop, remove)
+      refreshButton.Enabled = startButton.Enabled = pauseButton.Enabled = stopButton.Enabled = removeButton.Enabled = false;
+
+      if (enabled && progress.ProgressState != ProgressState.Started) {
+        var jobs = GetSelectedJobs().ToList();
+        if (jobs.Any()) {
+                    
+          startButton.Enabled = jobs.All(x =>
+            !x.IsProgressing && HiveAdminClient.Instance.Tasks.ContainsKey(x.Id) && HiveAdminClient.Instance.Tasks[x.Id].Count > 0
+            && x.Job.ProjectId != Guid.Empty //&& x.Job.ResourceIds != null && x.Job.ResourceIds.Any()
+            && (x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused));
+          pauseButton.Enabled = jobs.All(x => !x.IsProgressing && x.ExecutionState == ExecutionState.Started);
+          stopButton.Enabled = jobs.All(x => !x.IsProgressing && (x.ExecutionState == ExecutionState.Started || x.ExecutionState == ExecutionState.Paused));
+          removeButton.Enabled = jobs.All(x => !x.IsProgressing && x.Job.State == JobState.Online);
+        }
+      }
+
+      // refresh Button
+      if (Content != null && !Locked && progress.ProgressState != ProgressState.Started) {
+        refreshButton.Enabled = true;
+      }
+    }
+
+    #endregion Overrides
+
+    #region Event Handlers
+    private void ProjectJobsView_Load(object sender, EventArgs e) {
+      
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      progress.Start("Refreshing jobs...");
+      SetEnabledStateOfControls();
+      var task = System.Threading.Tasks.Task.Factory.StartNew(RefreshJobsAsync);
+
+      task.ContinueWith((t) => {
+        progress.Finish();        
+        SetEnabledStateOfControls();
+      });
+    }
+
+    private void removeButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to remove following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Removing job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(RemoveJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured removing the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    private void startButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to resume following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Resuming job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(ResumeJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();            
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured resuming the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    private void pauseButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to pause following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Pausing job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(PauseJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();            
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured pausing the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    private void stopButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to stop following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Stopping job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(StopJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured stopping the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);            
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    public event EventHandler IsProgressingChanged;
+    private void OnIsProgressingChanged() {
+      var handler = IsProgressingChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    #endregion Event Handlers
+
+    #region Helpers
+
+    private IEnumerable<RefreshableJob> GetSelectedJobs() {
+      if (Content == null || matrixView.DataGridView.SelectedRows == null || matrixView.DataGridView.SelectedRows.Count == 0)
+        return Enumerable.Empty<RefreshableJob>() ;
+
+      var jobs = new List<RefreshableJob>();
+      foreach (DataGridViewRow r in matrixView.DataGridView.SelectedRows) {
+        if (((string)r.Cells[0].Value) == JobState.Online.ToString()) {
+          jobs.Add(HiveAdminClient.Instance.Jobs[Content.Id].FirstOrDefault(x => x.Id == Guid.Parse((string) r.Cells[11].Value)));
+        }
+      }
+
+      return jobs;
+    }
+
+    private void RefreshJobs() {
+      HiveAdminClient.Instance.RefreshJobs();
+      UpdateJobs();
+      SetEnabledStateOfControls();
+    }
+
+    private StringMatrix CreateValueMatrix() {
+      if (Content == null || Content.Id == Guid.Empty)
+        return new StringMatrix();
+
+      var jobs = HiveAdminClient.Instance.Jobs[Content.Id];
+      var resources = HiveAdminClient.Instance.Resources;
+      string[,] values = new string[jobs.Count, 13];
+
+      for(int i = 0; i < jobs.Count; i++) {
+        var job = jobs.ElementAt(i);        
+        values[i, 0] = job.Job.State.ToString();
+        values[i, 1] = job.ExecutionState.ToString();
+        values[i, 2] = job.ExecutionTime.ToString();
+        values[i, 3] = job.Job.DateCreated.ToString();
+        values[i, 4] = job.Job.OwnerUsername;
+        values[i, 5] = job.Job.Name;
+        values[i, 6] = job.Job.JobCount.ToString();
+        values[i, 7] = (job.Job.JobCount - job.Job.CalculatingCount - job.Job.FinishedCount).ToString();
+        values[i, 8] = job.Job.CalculatingCount.ToString();
+        values[i, 9] = job.Job.FinishedCount.ToString();
+        values[i, 10] = job.Job.Description;        
+        values[i, 11] = job.Job.Id.ToString();
+        values[i, 12] = job.Job.OwnerUserId.ToString();
+      }
+      
+      var matrix = new StringMatrix(values);
+      matrix.ColumnNames = new string[] { JOB_STATE, JOB_EXECUTIONSTATE, JOB_EXECUTIONTIME, JOB_DATECREATED, JOB_OWNER, JOB_NAME, JOB_TASKCOUNT, JOB_WAITINGTASKCOUNT, JOB_CALCULATINGTASKCOUNT, JOB_FINISHEDTASKCOUNT, JOB_DESCRIPTION, JOB_ID, JOB_OWNERID };
+      matrix.SortableView = true;
+      return matrix;
+    }
+    
+    private void UpdateJobs() {
+      if (InvokeRequired) Invoke((Action)UpdateJobs);
+      else {
+        if(Content != null && Content.Id != null && Content.Id != Guid.Empty) {
+          var matrix = CreateValueMatrix();
+          matrixView.Content = matrix;
+          if(matrix != null) {
+            foreach (DataGridViewRow row in matrixView.DataGridView.Rows) {
+              string val = ((string)row.Cells[0].Value);
+              if (val == JobState.Online.ToString()) {
+                row.DefaultCellStyle.BackColor = onlineStatusColor;
+              } else if (val == JobState.StatisticsPending.ToString()) {
+                row.DefaultCellStyle.BackColor = statisticsPendingStatusColor;
+              } else if (val == JobState.DeletionPending.ToString()) {
+                row.DefaultCellStyle.BackColor = deletionPendingStatusColor;
+              }
+            }
+
+            matrixView.DataGridView.AutoResizeColumns();
+            matrixView.DataGridView.Columns[0].MinimumWidth = 90;
+            matrixView.DataGridView.Columns[1].MinimumWidth = 108;
+          }
+        } 
+      }
+    }
+
+    private void RefreshJobsAsync() {
+      HiveAdminClient.Instance.RefreshJobs();
+      UpdateJobs();
+    }
+
+    private void ResumeJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>)jobs;
+      foreach (var job in jobList) {
+        progress.Status = "Resuming job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.ResumeJob(job);
+      }
+    }
+
+    private void PauseJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>)jobs;
+      foreach (var job in jobList) {
+        progress.Status = "Pausing job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.PauseJob(job);
+      }
+    }
+
+    private void StopJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>) jobs;
+      foreach (var job in jobList) {        
+        progress.Status = "Stopping job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.StopJob(job);
+      }
+    }
+
+    private void RemoveJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>)jobs;
+      progress.Start();
+      foreach (var job in jobList) {
+        progress.Status = "Removing job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.RemoveJob(job);
+      }
+      progress.Finish();
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion Helpers
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs	(revision 16240)
@@ -0,0 +1,195 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectPermissionsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.inheritButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.permissionsGroupBox = new System.Windows.Forms.GroupBox();
+      this.treeView = new Hive.Views.TreeView.NoDoubleClickTreeView();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.detailsGroupBox = new System.Windows.Forms.GroupBox();
+      this.detailsViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.permissionsGroupBox.SuspendLayout();
+      this.detailsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // inheritButton
+      // 
+      this.inheritButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.OrgChart;
+      this.inheritButton.Location = new System.Drawing.Point(33, 3);
+      this.inheritButton.Name = "inheritButton";
+      this.inheritButton.Size = new System.Drawing.Size(24, 24);
+      this.inheritButton.TabIndex = 2;
+      this.toolTip.SetToolTip(this.inheritButton, "Save and hand down permission settings to all descendant projects");
+      this.inheritButton.UseVisualStyleBackColor = true;
+      this.inheritButton.Click += new System.EventHandler(this.inheritButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveButton.Location = new System.Drawing.Point(63, 3);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(24, 24);
+      this.saveButton.TabIndex = 3;
+      this.toolTip.SetToolTip(this.saveButton, "Save permission settings");
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 33);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.permissionsGroupBox);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.detailsGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(824, 458);
+      this.splitContainer.SplitterDistance = 274;
+      this.splitContainer.TabIndex = 6;
+      // 
+      // permissionsGroupBox
+      // 
+      this.permissionsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.permissionsGroupBox.Controls.Add(this.treeView);
+      this.permissionsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.permissionsGroupBox.Name = "permissionsGroupBox";
+      this.permissionsGroupBox.Size = new System.Drawing.Size(268, 452);
+      this.permissionsGroupBox.TabIndex = 4;
+      this.permissionsGroupBox.TabStop = false;
+      this.permissionsGroupBox.Text = "Permissions (Assigned + Included)";
+      // 
+      // treeView
+      // 
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageList;
+      this.treeView.Location = new System.Drawing.Point(6, 19);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(256, 427);
+      this.treeView.TabIndex = 5;
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterCheck);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
+      // 
+      // detailsGroupBox
+      // 
+      this.detailsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsGroupBox.Controls.Add(this.detailsViewHost);
+      this.detailsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.detailsGroupBox.Name = "detailsGroupBox";
+      this.detailsGroupBox.Size = new System.Drawing.Size(540, 452);
+      this.detailsGroupBox.TabIndex = 6;
+      this.detailsGroupBox.TabStop = false;
+      this.detailsGroupBox.Text = "Details";
+      // 
+      // detailsViewHost
+      // 
+      this.detailsViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsViewHost.Caption = "View";
+      this.detailsViewHost.Content = null;
+      this.detailsViewHost.Enabled = false;
+      this.detailsViewHost.Location = new System.Drawing.Point(6, 19);
+      this.detailsViewHost.Name = "detailsViewHost";
+      this.detailsViewHost.ReadOnly = true;
+      this.detailsViewHost.Size = new System.Drawing.Size(528, 427);
+      this.detailsViewHost.TabIndex = 7;
+      this.detailsViewHost.ViewsLabelVisible = true;
+      this.detailsViewHost.ViewType = null;
+      // 
+      // ProjectPermissionsView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.saveButton);
+      this.Controls.Add(this.inheritButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProjectPermissionsView";
+      this.Size = new System.Drawing.Size(830, 494);
+      this.Load += new System.EventHandler(this.ProjectPermissionsView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.permissionsGroupBox.ResumeLayout(false);
+      this.detailsGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button inheritButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.ImageList imageList;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox permissionsGroupBox;
+    private HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView treeView;
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.GroupBox detailsGroupBox;
+    private MainForm.WindowsForms.ViewHost detailsViewHost;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs	(revision 16240)
@@ -0,0 +1,338 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectPermissionsView : ItemView {
+    private const int userImageIndex = 0;
+    private const int userGroupImageIndex = 1;
+
+    private readonly HashSet<UserGroupBase> assignedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> inheritedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> newAssignedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> newInheritedPermissions = new HashSet<UserGroupBase>();
+    private readonly Dictionary<Guid, HashSet<UserGroupBase>> userGroupAncestors = new Dictionary<Guid, HashSet<UserGroupBase>>();
+    private readonly Dictionary<Guid, HashSet<UserGroupBase>> userGroupDescendants = new Dictionary<Guid, HashSet<UserGroupBase>>();
+
+    private IEnumerable<UserGroupBase> addedPermissions;
+    private IEnumerable<UserGroupBase> removedPermissions;
+    private IEnumerable<UserGroupBase> addedIncludes;
+    private IEnumerable<UserGroupBase> removedIncludes;
+
+    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
+    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
+    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
+    private readonly Color projectOwnerColor = Color.DarkRed;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectPermissionsView() {
+      InitializeComponent();
+
+      treeView.ImageList.Images.Add(VSImageLibrary.User);
+      treeView.ImageList.Images.Add(VSImageLibrary.UserAccounts);
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        assignedPermissions.Clear();
+        inheritedPermissions.Clear();
+        newAssignedPermissions.Clear();
+        newInheritedPermissions.Clear();
+        treeView.Nodes.Clear();
+        detailsViewHost.Content = null;
+      } else {
+        UpdatePermissionList();        
+      }
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+
+      inheritButton.Enabled = enabled;
+      saveButton.Enabled = enabled;
+      treeView.Enabled = enabled;
+
+      if (detailsViewHost != null) {
+        detailsViewHost.Locked = true;
+      }
+
+    }
+    #endregion
+
+    #region Event Handlers
+    private void ProjectPermissionsView_Load(object sender, EventArgs e) {
+
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      UpdatePermissionList();
+    }
+
+    private async void inheritButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => SetGrantedProjectPermissions(Content.Id, newAssignedPermissions.Select(x => x.Id), false, true, false));
+      UpdatePermissionList();
+    }
+
+    private async void saveButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => SetGrantedProjectPermissions(Content.Id, newAssignedPermissions.Select(x => x.Id), false, false, false));
+      UpdatePermissionList();
+    }
+
+    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var selectedPermission = (UserGroupBase)e.Node.Tag;
+      detailsViewHost.Content = selectedPermission;
+      if (selectedPermission is LightweightUser)
+        detailsViewHost.ViewType = typeof(Access.Views.RefreshableLightweightUserInformationView);
+    }
+
+    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      var checkedPermission = (UserGroupBase)e.Node.Tag;
+      if (e.Node.Parent == null 
+        || newInheritedPermissions.Contains(checkedPermission) 
+        || checkedPermission.Id == Guid.Empty
+        || Content.OwnerUserId == checkedPermission.Id)
+          e.Cancel = true;
+    }
+
+    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedPermission = (UserGroupBase)e.Node.Tag;
+      if (e.Node.Checked)
+        newAssignedPermissions.Add(checkedPermission);
+      else
+        newAssignedPermissions.Remove(checkedPermission);
+
+      UpdateNewPermissionList();
+    }
+    #endregion
+
+    #region Helpers
+    private void UpdatePermissionList() {
+      AccessClient.Instance.Refresh();
+
+      UpdateUserGroupGenealogy();
+      UpdateAssignedPermissions();
+      UpdateInheritedPermissions();
+      var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+      detailsViewHost.Content = top;
+    }
+
+    private void UpdateNewPermissionList() {
+      UpdateNewAssignedPermissions();
+      UpdateNewInheritedPermissions();
+      var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+      detailsViewHost.Content = top;
+    }
+
+    private void UpdateAssignedPermissions() {
+      assignedPermissions.Clear();
+      newAssignedPermissions.Clear();
+      var grantedPermissions = GetGrantedPermissionsForProject(Content.Id);
+      foreach (var r in grantedPermissions) {
+        assignedPermissions.Add(r);
+        newAssignedPermissions.Add(r);
+      }
+    }
+
+    private void UpdateNewAssignedPermissions() {
+      for(int i = newAssignedPermissions.Count-1; i >= 0; i--) {
+        if(newAssignedPermissions.Intersect(userGroupAncestors[newAssignedPermissions.ElementAt(i).Id]).Any() 
+          && newAssignedPermissions.ElementAt(i).Id != Content.OwnerUserId) {
+          newAssignedPermissions.Remove(newAssignedPermissions.ElementAt(i));
+        }
+      }
+    }
+
+    private void UpdateInheritedPermissions() {
+      inheritedPermissions.Clear();
+      newInheritedPermissions.Clear();
+      foreach(var item in assignedPermissions) {
+        if(userGroupDescendants.ContainsKey(item.Id)) {
+          foreach(var descendant in userGroupDescendants[item.Id]) {
+            if(!assignedPermissions.Contains(descendant)) {
+              inheritedPermissions.Add(descendant);
+              newInheritedPermissions.Add(descendant);
+            }
+          }
+        }
+      }
+    }
+
+    private void UpdateNewInheritedPermissions() {
+      newInheritedPermissions.Clear();
+      foreach(var item in newAssignedPermissions) {
+        if(userGroupDescendants.ContainsKey(item.Id)) {
+          foreach(var descendant in userGroupDescendants[item.Id]) {
+            if(!newAssignedPermissions.Contains(descendant))
+              newInheritedPermissions.Add(descendant);
+          }
+        }
+      }
+    }
+
+    private void UpdateUserGroupGenealogy() {
+      userGroupAncestors.Clear();
+      userGroupDescendants.Clear();
+
+      var usersAndGroups = AccessClient.Instance.UsersAndGroups;
+      foreach (var ug in usersAndGroups) {
+        userGroupAncestors.Add(ug.Id, new HashSet<UserGroupBase>());
+        userGroupDescendants.Add(ug.Id, new HashSet<UserGroupBase>());
+      }
+
+      var userGroupTree = HiveServiceLocator.Instance.CallHiveService(s => s.GetUserGroupTree());
+      foreach(var branch in userGroupTree) {
+        var parent = usersAndGroups.Where(x => x.Id == branch.Key).SingleOrDefault();
+        if(parent != null) {
+          var userGroupsToAdd = usersAndGroups.Where(x => userGroupTree[parent.Id].Contains(x.Id));
+          foreach (var node in userGroupsToAdd) {
+            userGroupDescendants[parent.Id].Add(node);
+            userGroupAncestors[node.Id].Add(parent);
+          }
+        }
+      }
+    }
+
+    private static IEnumerable<UserGroupBase> GetGrantedPermissionsForProject(Guid projectId) {
+      if (projectId == Guid.Empty) return Enumerable.Empty<UserGroupBase>();
+      var projectPermissions = HiveServiceLocator.Instance.CallHiveService(s => s.GetProjectPermissions(projectId));
+      var userIds = new HashSet<Guid>(projectPermissions.Select(x => x.GrantedUserId));
+      return AccessClient.Instance.UsersAndGroups.Where(x => userIds.Contains(x.Id));
+    }
+
+    private static void SetGrantedProjectPermissions(Guid projectId, IEnumerable<Guid> userIds, bool reassign, bool cascading, bool reassignCascading) {
+      if (projectId == null || userIds == null) return;
+      HiveServiceLocator.Instance.CallHiveService(s => {
+        s.SaveProjectPermissions(projectId, userIds.ToList(), reassign, cascading, reassignCascading);
+      });
+    }
+
+    private UserGroupBase BuildPermissionsList(IEnumerable<UserGroupBase> usersAndGroups) {
+      addedPermissions = newAssignedPermissions.Except(assignedPermissions);
+      removedPermissions = assignedPermissions.Except(newAssignedPermissions);
+      addedIncludes = newInheritedPermissions.Except(inheritedPermissions);
+      removedIncludes = inheritedPermissions.Except(newInheritedPermissions);
+
+      treeView.Nodes.Clear();
+      if (!usersAndGroups.Any()) return null;
+
+      treeView.BeforeCheck -= treeView_BeforeCheck;
+      treeView.AfterCheck -= treeView_AfterCheck;
+
+      var userGroups = new HashSet<UserGroup>(usersAndGroups.OfType<UserGroup>());
+      var users = new HashSet<LightweightUser>(usersAndGroups.OfType<LightweightUser>());
+      UserGroupBase first = null;
+
+      var groupsNode = new TreeNode("Groups") { ForeColor = SystemColors.GrayText };
+      groupsNode.ImageIndex = groupsNode.SelectedImageIndex = userGroupImageIndex;
+
+      foreach (var group in userGroups.OrderBy(x => x.Name)) {
+        var node = new TreeNode(group.Name) { Tag = group };
+        node.ImageIndex = userGroupImageIndex;
+        node.SelectedImageIndex = node.ImageIndex;
+        BuildNode(group, node);
+        groupsNode.Nodes.Add(node);
+        if (first == null) first = group;
+      }
+
+      var usersNode = new TreeNode("Users") { ForeColor = SystemColors.GrayText };
+      usersNode.ImageIndex = usersNode.SelectedImageIndex = userImageIndex;
+
+      foreach (var user in users.OrderBy(x => x.ToString())) {
+        var node = new TreeNode(user.ToString()) { Tag = user };
+        node.ImageIndex = userImageIndex;
+        node.SelectedImageIndex = node.ImageIndex;
+        BuildNode(user, node);
+        usersNode.Nodes.Add(node);
+        if (first == null) first = user;
+      }
+
+      treeView.Nodes.Add(groupsNode);
+      treeView.Nodes.Add(usersNode);
+      treeView.BeforeCheck += treeView_BeforeCheck;
+      treeView.AfterCheck += treeView_AfterCheck;
+      treeView.ExpandAll();
+
+      return first;
+    }
+
+    private void BuildNode(UserGroupBase ug, TreeNode node) {
+
+      if (newAssignedPermissions.Contains(ug)) {
+        node.Checked = true;
+      } else if (newInheritedPermissions.Contains(ug)) {
+        node.Checked = true;
+        node.ForeColor = SystemColors.GrayText;
+      }
+
+      if (inheritedPermissions.Contains(ug) && newInheritedPermissions.Contains(ug)) {
+        node.Text += " [included]";
+      } else if (addedIncludes.Contains(ug)) {
+        node.BackColor = addedIncludeColor;
+        node.ForeColor = SystemColors.GrayText;
+        node.Text += " [added include]";
+      } else if (removedIncludes.Contains(ug)) {
+        node.BackColor = removedIncludeColor;
+        node.ForeColor = SystemColors.GrayText;
+        node.Text += " [removed include]";
+      }
+
+      if (addedPermissions.Contains(ug)) {
+        node.BackColor = addedAssignmentColor;
+        node.ForeColor = SystemColors.ControlText;
+        node.Text += " [added assignment]";
+      } else if (removedPermissions.Contains(ug)) {
+        node.BackColor = removedAssignmentColor;
+        node.ForeColor = SystemColors.ControlText;
+        node.Text += " [removed assignment]";
+      }
+
+      if(Content != null && ug != null && ug.Id != Guid.Empty 
+        && Content.OwnerUserId == ug.Id) {
+        node.ForeColor = projectOwnerColor;
+      }
+
+    }
+
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs	(revision 16240)
@@ -0,0 +1,197 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectResourcesView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.inheritButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.resourcesGroupBox = new System.Windows.Forms.GroupBox();
+      this.treeView = new Hive.Views.TreeView.NoDoubleClickTreeView();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.detailsGroupBox = new System.Windows.Forms.GroupBox();
+      this.detailsViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.resourcesGroupBox.SuspendLayout();
+      this.detailsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // inheritButton
+      // 
+      this.inheritButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.OrgChart;
+      this.inheritButton.Location = new System.Drawing.Point(33, 3);
+      this.inheritButton.Name = "inheritButton";
+      this.inheritButton.Size = new System.Drawing.Size(24, 24);
+      this.inheritButton.TabIndex = 2;
+      this.toolTip.SetToolTip(this.inheritButton, "Save and hand down resource assignments to all descendant projects");
+      this.inheritButton.UseVisualStyleBackColor = true;
+      this.inheritButton.Click += new System.EventHandler(this.inheritButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveButton.Location = new System.Drawing.Point(63, 3);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(24, 24);
+      this.saveButton.TabIndex = 3;
+      this.toolTip.SetToolTip(this.saveButton, "Save resource assignments");
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 33);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.resourcesGroupBox);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.detailsGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(824, 458);
+      this.splitContainer.SplitterDistance = 274;
+      this.splitContainer.TabIndex = 5;
+      // 
+      // resourcesGroupBox
+      // 
+      this.resourcesGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.resourcesGroupBox.Controls.Add(this.treeView);
+      this.resourcesGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.resourcesGroupBox.Name = "resourcesGroupBox";
+      this.resourcesGroupBox.Size = new System.Drawing.Size(268, 452);
+      this.resourcesGroupBox.TabIndex = 0;
+      this.resourcesGroupBox.TabStop = false;
+      this.resourcesGroupBox.Text = "Resources (Assigned + Included)";
+      // 
+      // treeView
+      // 
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageList;
+      this.treeView.Location = new System.Drawing.Point(6, 19);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(256, 427);
+      this.treeView.TabIndex = 0;
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterCheck);
+      this.treeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeSelect);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // detailsGroupBox
+      // 
+      this.detailsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsGroupBox.Controls.Add(this.detailsViewHost);
+      this.detailsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.detailsGroupBox.Name = "detailsGroupBox";
+      this.detailsGroupBox.Size = new System.Drawing.Size(540, 452);
+      this.detailsGroupBox.TabIndex = 0;
+      this.detailsGroupBox.TabStop = false;
+      this.detailsGroupBox.Text = "Details";
+      // 
+      // detailsViewHost
+      // 
+      this.detailsViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsViewHost.Caption = "View";
+      this.detailsViewHost.Content = null;
+      this.detailsViewHost.Enabled = false;
+      this.detailsViewHost.Location = new System.Drawing.Point(6, 19);
+      this.detailsViewHost.Name = "detailsViewHost";
+      this.detailsViewHost.ReadOnly = true;
+      this.detailsViewHost.Size = new System.Drawing.Size(528, 427);
+      this.detailsViewHost.TabIndex = 0;
+      this.detailsViewHost.ViewsLabelVisible = true;
+      this.detailsViewHost.ViewType = null;
+      // 
+      // ProjectResourcesView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.saveButton);
+      this.Controls.Add(this.inheritButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProjectResourcesView";
+      this.Size = new System.Drawing.Size(830, 494);
+      this.Load += new System.EventHandler(this.ProjectResourcesView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.resourcesGroupBox.ResumeLayout(false);
+      this.detailsGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button inheritButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox resourcesGroupBox;
+    private System.Windows.Forms.GroupBox detailsGroupBox;
+    private HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView treeView;
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.ImageList imageList;
+    private MainForm.WindowsForms.ViewHost detailsViewHost;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs	(revision 16240)
@@ -0,0 +1,522 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+using System.Collections;
+using HeuristicLab.Common;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectResourcesView : ItemView {
+    private const int slaveImageIndex = 0;
+    private const int slaveGroupImageIndex = 1;
+    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
+    public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
+    public const string IMMUTABLE_TAG = " [assigned, immutable]";
+    public const string INCLUDED_TAG = " [included]";
+    public const string ADDED_ASSIGNMENT_TAG = " [added assignment]";
+    public const string REMOVED_ASSIGNMENT_TAG = " [removed assignment]";
+    public const string ADDED_INCLUDE_TAG = " [added include]";
+    public const string REMOVED_INCLUDE_TAG = " [removed include]";
+
+    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> newAssignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> includedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> newIncludedResources = new HashSet<Resource>();
+
+    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
+    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
+    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
+    private HashSet<Resource> projectExclusiveResources = new HashSet<Resource>();
+    private TreeNode ungroupedGroupNode;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectResourcesView() {
+      InitializeComponent();
+
+      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
+      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
+
+      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
+      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
+    }
+
+    #region Overrides
+    protected override void OnClosing(FormClosingEventArgs e) {
+      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
+      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
+      base.OnClosing(e);
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        assignedResources.Clear();
+        newAssignedResources.Clear();
+        includedResources.Clear();
+        treeView.Nodes.Clear();
+        detailsViewHost.Content = null;
+      } else {
+        UpdateAssignedResources();
+        UpdateIncludedResources();
+        detailsViewHost.Content = BuildResourceTree();
+      }
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+
+      inheritButton.Enabled = enabled;
+      saveButton.Enabled = enabled;
+      treeView.Enabled = enabled;
+
+      if (detailsViewHost != null) {
+        detailsViewHost.Locked = true;
+      }
+    }
+    #endregion
+
+    #region Event Handlers
+    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
+      else {
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void ProjectResourcesView_Load(object sender, EventArgs e) {
+
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      HiveAdminClient.Instance.Refresh();
+      UpdateAssignedResources();
+      UpdateIncludedResources();
+      var top = BuildResourceTree();
+      detailsViewHost.Content = top;
+    }
+
+    private async void inheritButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, true, false);
+        });
+      HiveAdminClient.Instance.Refresh();
+      UpdateResourceTree();
+    }
+
+    private async void saveButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, false, false);
+        });
+      HiveAdminClient.Instance.Refresh();
+      UpdateResourceTree();
+    }
+
+    private void treeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      if (e.Node == null || e.Node == ungroupedGroupNode)
+        e.Cancel = true;
+
+      var selectedResource = (Resource)e.Node.Tag;
+      if(HiveAdminClient.Instance.DisabledParentResources.Contains(selectedResource))
+        e.Cancel = true;
+    }
+
+    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var selectedResource = (Resource)e.Node.Tag;
+      detailsViewHost.Content = selectedResource;
+    }
+
+    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      if (e.Node == null || e.Node == ungroupedGroupNode) {
+        e.Cancel = true;
+        return;
+      }
+        
+      var checkedResource = (Resource)e.Node.Tag;
+      if (checkedResource == null
+        || checkedResource.Id == Guid.Empty
+        || HiveAdminClient.Instance.DisabledParentResources.Contains(checkedResource) 
+        || newIncludedResources.Contains(checkedResource)) {
+        e.Cancel = true;
+      } else if (!IsAdmin()) {
+          if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id) 
+            || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any() 
+            || projectExclusiveResources.Contains(checkedResource)) {
+            e.Cancel = true;
+          }
+      }
+    }
+
+    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedResource = (Resource)e.Node.Tag;
+      if (e.Node.Checked) {
+        newAssignedResources.Add(checkedResource);
+      } else {
+        newAssignedResources.Remove(checkedResource);
+      }
+
+      UpdateNewResourceTree();
+    }
+    #endregion
+
+    #region Helpers
+
+    private void UpdateResourceTree() {
+      UpdateAssignedResources();
+      UpdateIncludedResources();
+
+      var top = BuildResourceTree();
+      detailsViewHost.Content = top;
+    }
+
+    private void UpdateNewResourceTree() {
+      UpdateNewAssignedResources();
+      UpdateNewIncludedResources();
+      var top = BuildResourceTree();
+      detailsViewHost.Content = top;
+    }
+
+    private IEnumerable<Resource> GetAvailableResourcesForProjectAdministration(Guid projectId) {
+      projectExclusiveResources.Clear();
+      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
+      var resources = HiveAdminClient.Instance.Resources.Where(x => x.Id != Guid.Empty);
+      if (!resources.Any()) return resources;
+      if (IsAdmin()) return resources;
+
+      // get project specific assigned resources
+      var projectResources = resources.Where(x => 
+        HiveAdminClient.Instance.ProjectResourceAssignments
+          .Where(a => a.ProjectId == projectId)
+          .Select(a => a.ResourceId)
+        .Contains(x.Id));
+
+      // look up for assignments of ancestor projects
+      var projectIds = new HashSet<Guid>();
+      HiveAdminClient.Instance.GetAvailableProjectAncestors(projectId).ToList().ForEach(x => projectIds.Add(x.Id));
+
+      var ancestorProjectResources = resources.Where(x => 
+        HiveAdminClient.Instance.ProjectResourceAssignments
+          .Where(a => projectIds.Contains(a.ProjectId))
+          .Select(a => a.ResourceId)
+        .Contains(x.Id));
+
+      // look down for included resources of ancestor projects
+      HashSet<Resource> availableResources = new HashSet<Resource>(ancestorProjectResources);
+      foreach (var r in ancestorProjectResources) {
+        foreach(var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
+          availableResources.Add(d);
+        }
+      }
+
+      // determine resources, which are exclusively assigned to the currently selected project
+      projectResources
+        .Except(availableResources)
+        .ToList()
+        .ForEach(x => projectExclusiveResources.Add(x));
+
+      // look down for included resources of currently selected project
+      if (projectExclusiveResources.Any()) {
+        foreach (var r in projectExclusiveResources.ToArray()) {
+          foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
+            projectExclusiveResources.Add(d);
+          }  
+        }
+      }
+
+      return availableResources.Union(projectExclusiveResources);
+    }
+
+    private IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
+      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
+      return HiveAdminClient.Instance.Resources.Where(x =>
+        HiveAdminClient.Instance.ProjectResourceAssignments
+          .Where(a => a.ProjectId == projectId)
+          .Select(a => a.ResourceId)
+        .Contains(x.Id));
+    }
+
+    private void SetAssignedProjectResources(Guid projectId, IEnumerable<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading) {
+      if (projectId == null || resourceIds == null) return;
+      HiveServiceLocator.Instance.CallHiveService(s => {
+       s.SaveProjectResourceAssignments(projectId, resourceIds.ToList(), reassign, cascading, reassignCascading);
+      });
+    }
+
+    private void UpdateNewAssignedResources() {
+      for(int i = newAssignedResources.Count -1; i >= 0; i--) {
+        if(newAssignedResources.Intersect(HiveAdminClient.Instance.GetAvailableResourceAncestors(newAssignedResources.ElementAt(i).Id)).Any()) {
+          newAssignedResources.Remove(newAssignedResources.ElementAt(i));
+        }
+      }
+    }
+
+    private void UpdateAssignedResources() {
+      assignedResources.Clear();
+      newAssignedResources.Clear();
+      foreach (var r in GetAssignedResourcesForProject(Content.Id)) {
+        assignedResources.Add(r);
+        newAssignedResources.Add(r);
+      }
+    }
+
+    private void UpdateIncludedResources() {
+      includedResources.Clear();
+      newIncludedResources.Clear();
+      foreach (var a in assignedResources) {
+        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
+          includedResources.Add(r);
+          newIncludedResources.Add(r);
+        }
+      }
+    }
+
+    private void UpdateNewIncludedResources() {
+      newIncludedResources.Clear();
+      foreach (var a in newAssignedResources) {
+        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
+          newIncludedResources.Add(r);
+        }
+      }
+    }
+
+    private Resource BuildResourceTree() {
+      treeView.Nodes.Clear();
+
+      treeView.BeforeCheck -= treeView_BeforeCheck;
+      treeView.AfterCheck -= treeView_AfterCheck;
+
+      var resources = GetAvailableResourcesForProjectAdministration(Content.Id);
+
+      var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
+      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>().Where(x => x.ParentResourceId == null));
+      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
+      //mainResources.UnionWith(parentedMainResources);
+      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
+      mainResources.UnionWith(mainDisabledParentResources);
+      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
+
+      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
+      
+      Resource top = null;
+      //bool nodeSelected = false;
+      if (detailsViewHost != null && detailsViewHost.Content != null && detailsViewHost.Content is Resource) {
+        var resourceId = ((Resource)detailsViewHost.Content).Id;
+        top = resources.Where(x => x.Id == resourceId).FirstOrDefault();
+      }
+      
+
+      TreeNode currentNode = null;
+      Resource currentResource = null;
+
+      var addedAssignments = newAssignedResources.Except(assignedResources);
+      var removedAssignments = assignedResources.Except(newAssignedResources);
+      var addedIncludes = newIncludedResources.Except(includedResources);
+      var removedIncludes = includedResources.Except(newIncludedResources);
+
+      while (stack.Any()) {
+        var newResource = stack.Pop();
+        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
+
+        if(top == null && !disabledParentResources.Contains(newResource)) {
+          top = newResource;
+        }
+
+        //if(!nodeSelected && top != null && newResource.Id == top.Id) {
+        //  newNode.BackColor = selectedBackColor;
+        //  newNode.ForeColor = selectedForeColor;
+        //  nodeSelected = true;
+        //}
+
+        // search for parent node of newNode and save in currentNode
+        // necessary since newNodes (stack top items) might be siblings
+        // or grand..grandparents of previous node (currentNode)
+        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
+          currentNode = currentNode.Parent;
+          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          treeView.Nodes.Add(newNode);
+        } else {
+          currentNode.Nodes.Add(newNode);
+        }
+
+        if (disabledParentResources.Contains(newResource)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        } else if (newAssignedResources.Contains(newResource)) {
+          newNode.Checked = true;
+          if(!HiveRoles.CheckAdminUserPermissions()) {
+            if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id) 
+              || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any() 
+              || projectExclusiveResources.Contains(newResource)) {
+              newNode.ForeColor = SystemColors.GrayText;
+              newNode.Text += IMMUTABLE_TAG;
+            }
+          }
+
+        } else if (newIncludedResources.Contains(newResource)) {
+          newNode.Checked = true;
+          newNode.ForeColor = SystemColors.GrayText;
+        }
+
+        if (includedResources.Contains(newResource) && newIncludedResources.Contains(newResource)) {
+          newNode.Text += INCLUDED_TAG;
+        } else if (addedIncludes.Contains(newResource)) {
+          newNode.BackColor = addedIncludeColor;
+          newNode.ForeColor = SystemColors.GrayText;
+          newNode.Text += ADDED_INCLUDE_TAG;
+        } else if (removedIncludes.Contains(newResource)) {
+          newNode.BackColor = removedIncludeColor;
+          newNode.Text += REMOVED_INCLUDE_TAG;
+        }
+
+        if (addedAssignments.Contains(newResource)) {
+          newNode.BackColor = addedAssignmentColor;
+          newNode.ForeColor = SystemColors.ControlText;
+          newNode.Text += ADDED_ASSIGNMENT_TAG;
+        } else if (removedAssignments.Contains(newResource)) {
+          newNode.BackColor = removedAssignmentColor;
+          newNode.ForeColor = SystemColors.ControlText;
+          newNode.Text += REMOVED_ASSIGNMENT_TAG;
+        }
+
+        if (newResource is Slave) {
+          newNode.ImageIndex = slaveImageIndex;
+        } else {
+          newNode.ImageIndex = slaveGroupImageIndex;
+
+          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
+          if (childResources.Any()) {
+            foreach (var resource in childResources.OrderByDescending(x => x.Name)) {
+              subResources.Remove(resource);
+              stack.Push(resource);
+            }
+            currentNode = newNode;
+            currentResource = newResource;
+          }
+        }
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+      }
+
+      ExpandResourceNodesOfInterest(treeView.Nodes);
+
+      bool expandUngroupedGroupNode = false;
+      var ungroupedSlaves = subResources.OfType<Slave>().OrderBy(x => x.Name);
+      if(ungroupedSlaves.Any()) {
+        ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
+          ForeColor = SystemColors.GrayText,
+          Tag = new SlaveGroup() {
+            Name = UNGROUPED_GROUP_NAME,
+            Description = UNGROUPED_GROUP_DESCRIPTION
+          }
+        };
+
+        foreach (var slave in ungroupedSlaves) {
+          var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+          if (newAssignedResources.Contains(slave)) {
+            slaveNode.Checked = true;
+            expandUngroupedGroupNode = true;
+          }
+
+          if (!HiveRoles.CheckAdminUserPermissions()) {
+            slaveNode.ForeColor = SystemColors.GrayText;
+            slaveNode.Text += IMMUTABLE_TAG;
+          } else {
+            if (addedAssignments.Contains(slave)) {
+              slaveNode.BackColor = addedAssignmentColor;
+              slaveNode.ForeColor = SystemColors.ControlText;
+              slaveNode.Text += ADDED_ASSIGNMENT_TAG;
+            } else if (removedAssignments.Contains(slave)) {
+              slaveNode.BackColor = removedAssignmentColor;
+              slaveNode.ForeColor = SystemColors.ControlText;
+              slaveNode.Text += REMOVED_ASSIGNMENT_TAG;
+              expandUngroupedGroupNode = true;
+            }
+          }
+          ungroupedGroupNode.Nodes.Add(slaveNode);
+        }
+
+        if(expandUngroupedGroupNode) ungroupedGroupNode.Expand();
+        treeView.Nodes.Add(ungroupedGroupNode);
+      } else if (ungroupedGroupNode != null) {
+        ungroupedGroupNode.Nodes.Clear();
+      }
+
+      treeView.BeforeCheck += treeView_BeforeCheck;
+      treeView.AfterCheck += treeView_AfterCheck;          
+
+      return top;
+    }
+
+    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        Resource r = (Resource)n.Tag;
+        if (n.Nodes.Count > 0) {
+          if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
+            n.Expand();
+            ExpandResourceNodesOfInterest(n.Nodes);
+          } else {
+            n.Collapse();
+          }
+        } else {
+          n.Collapse();
+        }
+      }
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs	(revision 16240)
@@ -0,0 +1,253 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.idLabel = new System.Windows.Forms.Label();
+      this.idTextBox = new System.Windows.Forms.TextBox();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.descriptionLabel = new System.Windows.Forms.Label();
+      this.descriptionTextBox = new System.Windows.Forms.TextBox();
+      this.ownerComboBox = new System.Windows.Forms.ComboBox();
+      this.ownerLabel = new System.Windows.Forms.Label();
+      this.createdLabel = new System.Windows.Forms.Label();
+      this.startDateTimePicker = new System.Windows.Forms.DateTimePicker();
+      this.startLabel = new System.Windows.Forms.Label();
+      this.endDateTimePicker = new System.Windows.Forms.DateTimePicker();
+      this.endLabel = new System.Windows.Forms.Label();
+      this.indefiniteCheckBox = new System.Windows.Forms.CheckBox();
+      this.createdTextBox = new System.Windows.Forms.TextBox();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.SuspendLayout();
+      // 
+      // idLabel
+      // 
+      this.idLabel.AutoSize = true;
+      this.idLabel.Location = new System.Drawing.Point(3, 11);
+      this.idLabel.Name = "idLabel";
+      this.idLabel.Size = new System.Drawing.Size(19, 13);
+      this.idLabel.TabIndex = 0;
+      this.idLabel.Text = "Id:";
+      // 
+      // idTextBox
+      // 
+      this.idTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.idTextBox.Location = new System.Drawing.Point(72, 8);
+      this.idTextBox.Name = "idTextBox";
+      this.idTextBox.ReadOnly = true;
+      this.idTextBox.Size = new System.Drawing.Size(464, 20);
+      this.idTextBox.TabIndex = 1;
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(3, 37);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 0;
+      this.nameLabel.Text = "Name:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(72, 34);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.Size = new System.Drawing.Size(464, 20);
+      this.nameTextBox.TabIndex = 2;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
+      this.nameTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.nameTextBox_Validating);
+      // 
+      // descriptionLabel
+      // 
+      this.descriptionLabel.AutoSize = true;
+      this.descriptionLabel.Location = new System.Drawing.Point(3, 63);
+      this.descriptionLabel.Name = "descriptionLabel";
+      this.descriptionLabel.Size = new System.Drawing.Size(63, 13);
+      this.descriptionLabel.TabIndex = 0;
+      this.descriptionLabel.Text = "Description:";
+      // 
+      // descriptionTextBox
+      // 
+      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.descriptionTextBox.Location = new System.Drawing.Point(72, 60);
+      this.descriptionTextBox.Multiline = true;
+      this.descriptionTextBox.Name = "descriptionTextBox";
+      this.descriptionTextBox.Size = new System.Drawing.Size(464, 98);
+      this.descriptionTextBox.TabIndex = 3;
+      this.descriptionTextBox.TextChanged += new System.EventHandler(this.descriptionTextBox_TextChanged);
+      // 
+      // ownerLabel
+      // 
+      this.ownerLabel.AutoSize = true;
+      this.ownerLabel.Location = new System.Drawing.Point(3, 167);
+      this.ownerLabel.Name = "ownerLabel";
+      this.ownerLabel.Size = new System.Drawing.Size(41, 13);
+      this.ownerLabel.TabIndex = 0;
+      this.ownerLabel.Text = "Owner:";
+      // 
+      // ownerComboBox
+      // 
+      //this.ownerComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+      //      | System.Windows.Forms.AnchorStyles.Right)));
+      this.ownerComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+      this.ownerComboBox.FormattingEnabled = true;
+      this.ownerComboBox.Location = new System.Drawing.Point(72, 164);
+      this.ownerComboBox.Name = "ownerComboBox";
+      this.ownerComboBox.Size = new System.Drawing.Size(200, 21);
+      this.ownerComboBox.TabIndex = 4;
+      this.ownerComboBox.SelectedIndexChanged += new System.EventHandler(this.ownerComboBox_SelectedIndexChanged);
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(278, 162);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 5;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // createdLabel
+      // 
+      this.createdLabel.AutoSize = true;
+      this.createdLabel.Location = new System.Drawing.Point(3, 197);
+      this.createdLabel.Name = "createdLabel";
+      this.createdLabel.Size = new System.Drawing.Size(47, 13);
+      this.createdLabel.TabIndex = 8;
+      this.createdLabel.Text = "Created:";
+      // 
+      // startDateTimePicker
+      // 
+      this.startDateTimePicker.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
+      this.startDateTimePicker.Enabled = false;
+      this.startDateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.startDateTimePicker.Location = new System.Drawing.Point(72, 216);
+      this.startDateTimePicker.Name = "startDateTimePicker";
+      this.startDateTimePicker.Size = new System.Drawing.Size(200, 20);
+      this.startDateTimePicker.TabIndex = 9;
+      this.startDateTimePicker.ValueChanged += new System.EventHandler(this.startDateTimePicker_ValueChanged);      
+      // 
+      // startLabel
+      // 
+      this.startLabel.AutoSize = true;
+      this.startLabel.Location = new System.Drawing.Point(3, 223);
+      this.startLabel.Name = "startLabel";
+      this.startLabel.Size = new System.Drawing.Size(32, 13);
+      this.startLabel.TabIndex = 10;
+      this.startLabel.Text = "Start:";
+      // 
+      // endDateTimePicker
+      // 
+      this.endDateTimePicker.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
+      this.endDateTimePicker.Enabled = false;
+      this.endDateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.endDateTimePicker.Location = new System.Drawing.Point(72, 243);
+      this.endDateTimePicker.Name = "endDateTimePicker";
+      this.endDateTimePicker.Size = new System.Drawing.Size(200, 20);
+      this.endDateTimePicker.TabIndex = 11;
+      this.endDateTimePicker.ValueChanged += new System.EventHandler(this.endDateTimePicker_ValueChanged);
+      // 
+      // endLabel
+      // 
+      this.endLabel.AutoSize = true;
+      this.endLabel.Location = new System.Drawing.Point(3, 249);
+      this.endLabel.Name = "endLabel";
+      this.endLabel.Size = new System.Drawing.Size(29, 13);
+      this.endLabel.TabIndex = 10;
+      this.endLabel.Text = "End:";
+      // 
+      // indefiniteCheckBox
+      // 
+      this.indefiniteCheckBox.AutoSize = true;
+      this.indefiniteCheckBox.Location = new System.Drawing.Point(278, 245);
+      this.indefiniteCheckBox.Name = "indefiniteCheckBox";
+      this.indefiniteCheckBox.Size = new System.Drawing.Size(69, 17);
+      this.indefiniteCheckBox.TabIndex = 13;
+      this.indefiniteCheckBox.Text = "Indefinite";
+      this.indefiniteCheckBox.UseVisualStyleBackColor = true;
+      this.indefiniteCheckBox.CheckedChanged += new System.EventHandler(this.indefiniteCheckBox_CheckedChanged);
+      // 
+      // createdTextBox
+      // 
+      this.createdTextBox.Location = new System.Drawing.Point(72, 190);
+      this.createdTextBox.Name = "createdTextBox";
+      this.createdTextBox.ReadOnly = true;
+      this.createdTextBox.Size = new System.Drawing.Size(200, 20);
+      this.createdTextBox.TabIndex = 14;
+      // 
+      // ProjectView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.refreshButton);
+      this.Controls.Add(this.createdTextBox);
+      this.Controls.Add(this.indefiniteCheckBox);
+      this.Controls.Add(this.endLabel);
+      this.Controls.Add(this.endDateTimePicker);
+      this.Controls.Add(this.startLabel);
+      this.Controls.Add(this.startDateTimePicker);
+      this.Controls.Add(this.createdLabel);
+      this.Controls.Add(this.ownerLabel);
+      this.Controls.Add(this.ownerComboBox);
+      this.Controls.Add(this.descriptionTextBox);
+      this.Controls.Add(this.descriptionLabel);
+      this.Controls.Add(this.nameTextBox);
+      this.Controls.Add(this.nameLabel);
+      this.Controls.Add(this.idTextBox);
+      this.Controls.Add(this.idLabel);
+      this.Name = "ProjectView";
+      this.Size = new System.Drawing.Size(539, 271);
+      this.Load += new System.EventHandler(this.ProjectView_Load);
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Label idLabel;
+    protected System.Windows.Forms.TextBox idTextBox;
+    private System.Windows.Forms.Label nameLabel;
+    private System.Windows.Forms.TextBox nameTextBox;
+    private System.Windows.Forms.Label descriptionLabel;
+    private System.Windows.Forms.TextBox descriptionTextBox;
+    private System.Windows.Forms.ComboBox ownerComboBox;
+    private System.Windows.Forms.Label ownerLabel;
+    private System.Windows.Forms.Label createdLabel;
+    private System.Windows.Forms.DateTimePicker startDateTimePicker;
+    private System.Windows.Forms.Label startLabel;
+    private System.Windows.Forms.DateTimePicker endDateTimePicker;
+    private System.Windows.Forms.Label endLabel;
+    private System.Windows.Forms.CheckBox indefiniteCheckBox;
+    private System.Windows.Forms.TextBox createdTextBox;
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.ToolTip toolTip;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs	(revision 16240)
@@ -0,0 +1,333 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = true)]
+  public partial class ProjectView : ItemView {
+    private readonly object locker = new object();
+
+    private Guid persistedOwnerUserId;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; persistedOwnerUserId = Content != null ? Content.OwnerUserId : Guid.Empty; }
+    }
+
+    public ProjectView() {
+      InitializeComponent();
+
+      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
+      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
+    }
+
+    #region Overrides
+    protected override void OnClosing(FormClosingEventArgs e) {
+      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
+      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
+      base.OnClosing(e);
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.PropertyChanged += Content_PropertyChanged;
+    }
+
+    protected override void DeregisterContentEvents() {
+      Content.PropertyChanged -= Content_PropertyChanged;
+      base.DeregisterContentEvents();
+    }
+
+    protected void RegisterControlEvents() {
+      nameTextBox.TextChanged += nameTextBox_TextChanged;
+      nameTextBox.Validating += nameTextBox_Validating;
+      descriptionTextBox.TextChanged += descriptionTextBox_TextChanged;
+      ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+      startDateTimePicker.ValueChanged += startDateTimePicker_ValueChanged;
+      endDateTimePicker.ValueChanged += endDateTimePicker_ValueChanged;
+      indefiniteCheckBox.CheckedChanged += indefiniteCheckBox_CheckedChanged;
+    }
+
+    protected void DeregisterControlEvents() {
+      nameTextBox.TextChanged -= nameTextBox_TextChanged;
+      nameTextBox.Validating -= nameTextBox_Validating;
+      descriptionTextBox.TextChanged -= descriptionTextBox_TextChanged;
+      ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+      startDateTimePicker.ValueChanged -= startDateTimePicker_ValueChanged;
+      endDateTimePicker.ValueChanged -= endDateTimePicker_ValueChanged;
+      indefiniteCheckBox.CheckedChanged -= indefiniteCheckBox_CheckedChanged;
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      DeregisterControlEvents();
+      if (Content == null) {
+        idTextBox.Clear();
+        nameTextBox.Clear();
+        descriptionTextBox.Clear();
+        ownerComboBox.SelectedItem = null;
+        createdTextBox.Clear();
+        startDateTimePicker.Value = DateTime.Now;
+        endDateTimePicker.Value = startDateTimePicker.Value;
+        indefiniteCheckBox.Checked = false;
+      } else {
+        idTextBox.Text = Content.Id.ToString();
+        nameTextBox.Text = Content.Name;
+        descriptionTextBox.Text = Content.Description;
+
+        ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+        var users = AccessClient.Instance.UsersAndGroups.OfType<LightweightUser>();
+        if (!Content.ParentProjectId.HasValue) users = users.Where(x => x.Roles.Select(y => y.Name).Contains(HiveRoles.Administrator));
+        ownerComboBox.DataSource = users.ToList();
+        ownerComboBox.SelectedItem = users.FirstOrDefault(x => x.Id == Content.OwnerUserId);
+        ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+
+        createdTextBox.Text = Content.DateCreated.ToString("ddd, dd.MM.yyyy, HH:mm:ss");
+        startDateTimePicker.Value = Content.StartDate;
+
+        indefiniteCheckBox.Checked = !Content.EndDate.HasValue;
+        if (!indefiniteCheckBox.Checked) endDateTimePicker.Value = Content.EndDate.Value;
+        else endDateTimePicker.Value = Content.StartDate;
+        endDateTimePicker.Enabled = !indefiniteCheckBox.Checked;
+      }
+      SetEnabledStateOfControls();
+      RegisterControlEvents();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+      nameTextBox.Enabled = enabled;
+      descriptionTextBox.Enabled = enabled;
+      refreshButton.Enabled = enabled;
+      ownerComboBox.Enabled = enabled;
+      createdTextBox.Enabled = enabled;
+      startDateTimePicker.Enabled = enabled;
+      endDateTimePicker.Enabled = enabled && Content.EndDate.HasValue;
+      indefiniteCheckBox.Enabled = enabled;
+
+      if (Content != null) {
+        //var parentProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).LastOrDefault();
+        var parentProject = HiveAdminClient.Instance.Projects.FirstOrDefault(x => x.Id == Content.ParentProjectId);
+        if ((!IsAdmin() && (parentProject == null || parentProject.EndDate.HasValue))
+           || (IsAdmin() && parentProject != null && parentProject.EndDate.HasValue)) {
+          indefiniteCheckBox.Enabled = false;
+        }
+
+        if (Content.Id != Guid.Empty && !IsAdmin() && !HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)) {
+          ownerComboBox.Enabled = false;
+          startDateTimePicker.Enabled = false;
+          endDateTimePicker.Enabled = false;
+          indefiniteCheckBox.Enabled = false;
+        }
+      }
+    }
+    #endregion
+
+    #region Event Handlers
+    private void Content_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)Content_PropertyChanged, sender, e);
+      else OnContentChanged();
+    }
+
+    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private async void ProjectView_Load(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateUsers(),
+        finallyCallback: () => {
+          ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+          var users = AccessClient.Instance.UsersAndGroups.OfType<LightweightUser>();
+          if (Content != null && !Content.ParentProjectId.HasValue) users = users.Where(x => x.Roles.Select(y => y.Name).Contains(HiveRoles.Administrator));
+          ownerComboBox.DataSource = users.ToList();
+          ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+        });
+    }
+
+    private async void refreshButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!refreshButton.Enabled) return;
+        refreshButton.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateUsers(),
+        finallyCallback: () => {
+          ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+          var users = AccessClient.Instance.UsersAndGroups.OfType<LightweightUser>();
+          if (Content != null && !Content.ParentProjectId.HasValue) users = users.Where(x => x.Roles.Select(y => y.Name).Contains(HiveRoles.Administrator));
+          ownerComboBox.DataSource = users.ToList();
+          ownerComboBox.SelectedItem = users.FirstOrDefault(x => x.Id == persistedOwnerUserId);
+          ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+          refreshButton.Enabled = true;
+        });
+    }
+
+    private void nameTextBox_Validating(object sender, CancelEventArgs e) {
+      if (string.IsNullOrEmpty(nameTextBox.Text)) {
+        MessageBox.Show(
+          "Project must have a name.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Error);
+        e.Cancel = true;
+      }
+    }
+
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != nameTextBox.Text) {
+        DeregisterContentEvents();
+        Content.Name = nameTextBox.Text;
+        RegisterContentEvents();
+      }
+    }
+
+    private void descriptionTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Description != descriptionTextBox.Text) {
+        DeregisterContentEvents();
+        Content.Description = descriptionTextBox.Text;
+        RegisterContentEvents();
+      }
+    }
+
+    private void ownerComboBox_SelectedIndexChanged(object sender, EventArgs e) {
+      var selectedItem = (LightweightUser)ownerComboBox.SelectedItem;
+      var selectedOwnerUserId = selectedItem != null ? selectedItem.Id : Guid.Empty;
+      if (Content != null && Content.OwnerUserId != selectedOwnerUserId) {
+        DeregisterContentEvents();
+        Content.OwnerUserId = selectedOwnerUserId;
+        RegisterContentEvents();
+      }
+    }
+
+    private void startDateTimePicker_ValueChanged(object sender, EventArgs e) {
+      if (Content == null) return;
+      startDateTimePicker.ValueChanged -= startDateTimePicker_ValueChanged;
+
+      if (!IsAdmin()) {
+        var parentProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).LastOrDefault();
+        if (parentProject != null) {
+          if (startDateTimePicker.Value < parentProject.StartDate)
+            startDateTimePicker.Value = parentProject.StartDate;
+        } else {
+          startDateTimePicker.Value = Content.StartDate;
+        }
+      }
+
+      if (!Content.EndDate.HasValue || startDateTimePicker.Value > Content.EndDate)
+        endDateTimePicker.Value = startDateTimePicker.Value;
+      if (Content.StartDate != startDateTimePicker.Value) {
+        DeregisterContentEvents();
+        Content.StartDate = startDateTimePicker.Value;
+        RegisterContentEvents();
+      }
+
+      startDateTimePicker.ValueChanged += startDateTimePicker_ValueChanged;
+    }
+
+    private void endDateTimePicker_ValueChanged(object sender, EventArgs e) {
+      if (Content == null) return;
+      endDateTimePicker.ValueChanged -= endDateTimePicker_ValueChanged;
+
+      if (!IsAdmin()) {
+        var parentProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).LastOrDefault();
+        if (parentProject != null) {
+          if (parentProject.EndDate.HasValue && endDateTimePicker.Value > parentProject.EndDate.Value) {
+            endDateTimePicker.Value = parentProject.EndDate.Value;
+          }
+        } else if (Content.EndDate.HasValue) {
+          endDateTimePicker.Value = Content.EndDate.Value;
+        }
+      }
+
+      if (endDateTimePicker.Value < startDateTimePicker.Value)
+        endDateTimePicker.Value = startDateTimePicker.Value;
+      if (Content.EndDate != endDateTimePicker.Value) {
+        DeregisterContentEvents();
+        Content.EndDate = endDateTimePicker.Value;
+        RegisterContentEvents();
+      }
+
+      endDateTimePicker.ValueChanged += endDateTimePicker_ValueChanged;
+    }
+
+    private void indefiniteCheckBox_CheckedChanged(object sender, EventArgs e) {
+      if (Content == null) return;
+
+      var newEndDate = indefiniteCheckBox.Checked ? (DateTime?)null : endDateTimePicker.Value;
+      endDateTimePicker.Enabled = !indefiniteCheckBox.Checked;
+      if (Content.EndDate != newEndDate) {
+        DeregisterContentEvents();
+        Content.EndDate = newEndDate;
+        RegisterContentEvents();
+      }
+    }
+    #endregion
+
+    #region Helpers
+    private void UpdateUsers() {
+      try {
+        AccessClient.Instance.Refresh();
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs	(revision 16240)
@@ -0,0 +1,295 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.saveProjectButton = new System.Windows.Forms.Button();
+      this.removeButton = new System.Windows.Forms.Button();
+      this.addButton = new System.Windows.Forms.Button();
+      this.projectsTreeView = new System.Windows.Forms.TreeView();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.tabControl = new System.Windows.Forms.TabControl();
+      this.detailsTabPage = new System.Windows.Forms.TabPage();
+      this.projectView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectView();
+      this.permissionsTabPage = new System.Windows.Forms.TabPage();
+      this.projectPermissionsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectPermissionsView();
+      this.resourcesTabPage = new System.Windows.Forms.TabPage();
+      this.projectResourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectResourcesView();
+      this.jobsTabPage = new System.Windows.Forms.TabPage();
+      this.projectJobsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectJobsView();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.tabControl.SuspendLayout();
+      this.detailsTabPage.SuspendLayout();
+      this.permissionsTabPage.SuspendLayout();
+      this.resourcesTabPage.SuspendLayout();
+      this.jobsTabPage.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 3);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.refreshButton);
+      this.splitContainer.Panel1.Controls.Add(this.saveProjectButton);
+      this.splitContainer.Panel1.Controls.Add(this.removeButton);
+      this.splitContainer.Panel1.Controls.Add(this.addButton);
+      this.splitContainer.Panel1.Controls.Add(this.projectsTreeView);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.tabControl);
+      this.splitContainer.Size = new System.Drawing.Size(847, 547);
+      this.splitContainer.SplitterDistance = 249;
+      this.splitContainer.TabIndex = 0;
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 14;
+      this.toolTip.SetToolTip(this.refreshButton, "Fetch list from server");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // saveProjectButton
+      // 
+      this.saveProjectButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveProjectButton.Location = new System.Drawing.Point(93, 3);
+      this.saveProjectButton.Name = "saveProjectButton";
+      this.saveProjectButton.Size = new System.Drawing.Size(24, 24);
+      this.saveProjectButton.TabIndex = 12;
+      this.toolTip.SetToolTip(this.saveProjectButton, "Store project on the server");
+      this.saveProjectButton.UseVisualStyleBackColor = true;
+      this.saveProjectButton.Click += new System.EventHandler(this.saveProjectButton_Click);
+      // 
+      // removeButton
+      // 
+      this.removeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Remove;
+      this.removeButton.Location = new System.Drawing.Point(63, 3);
+      this.removeButton.Name = "removeButton";
+      this.removeButton.Size = new System.Drawing.Size(24, 24);
+      this.removeButton.TabIndex = 11;
+      this.toolTip.SetToolTip(this.removeButton, "Remove a project");
+      this.removeButton.UseVisualStyleBackColor = true;
+      this.removeButton.Click += new System.EventHandler(this.removeButton_Click);
+      // 
+      // addButton
+      // 
+      this.addButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Add;
+      this.addButton.Location = new System.Drawing.Point(33, 3);
+      this.addButton.Name = "addButton";
+      this.addButton.Size = new System.Drawing.Size(24, 24);
+      this.addButton.TabIndex = 10;
+      this.toolTip.SetToolTip(this.addButton, "Add a new project");
+      this.addButton.UseVisualStyleBackColor = true;
+      this.addButton.Click += new System.EventHandler(this.addButton_Click);
+      // 
+      // projectsTreeView
+      // 
+      this.projectsTreeView.AllowDrop = true;
+      this.projectsTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectsTreeView.ImageIndex = 0;
+      this.projectsTreeView.ImageList = this.imageList;
+      this.projectsTreeView.Location = new System.Drawing.Point(3, 33);
+      this.projectsTreeView.Name = "projectsTreeView";
+      this.projectsTreeView.SelectedImageIndex = 0;
+      this.projectsTreeView.Size = new System.Drawing.Size(243, 511);
+      this.projectsTreeView.TabIndex = 9;
+      this.projectsTreeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.projectsTreeView_ItemDrag);
+      this.projectsTreeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.projectsTreeView_BeforeSelect);
+      this.projectsTreeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.projectsTreeView_MouseDown);
+      this.projectsTreeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.projectsTreeView_DragDrop);
+      this.projectsTreeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.projectsTreeView_DragEnterOver);
+      this.projectsTreeView.DragOver += new System.Windows.Forms.DragEventHandler(this.projectsTreeView_DragEnterOver);
+      this.projectsTreeView.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.projectsTreeView_QueryContinueDrag);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // tabControl
+      // 
+      this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.tabControl.Controls.Add(this.detailsTabPage);
+      this.tabControl.Controls.Add(this.permissionsTabPage);
+      this.tabControl.Controls.Add(this.resourcesTabPage);
+      this.tabControl.Controls.Add(this.jobsTabPage);
+      this.tabControl.Location = new System.Drawing.Point(3, 3);
+      this.tabControl.Name = "tabControl";
+      this.tabControl.SelectedIndex = 0;
+      this.tabControl.Size = new System.Drawing.Size(585, 541);
+      this.tabControl.TabIndex = 0;
+      // 
+      // detailsTabPage
+      // 
+      this.detailsTabPage.Controls.Add(this.projectView);
+      this.detailsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.detailsTabPage.Name = "detailsTabPage";
+      this.detailsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.detailsTabPage.Size = new System.Drawing.Size(577, 515);
+      this.detailsTabPage.TabIndex = 0;
+      this.detailsTabPage.Text = "Details";
+      this.detailsTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectView
+      // 
+      this.projectView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectView.Caption = "ProjectView";
+      this.projectView.Content = null;
+      this.projectView.Location = new System.Drawing.Point(6, 6);
+      this.projectView.Name = "projectView";
+      this.projectView.ReadOnly = false;
+      this.projectView.Size = new System.Drawing.Size(565, 503);
+      this.projectView.TabIndex = 0;
+      // 
+      // permissionsTabPage
+      // 
+      this.permissionsTabPage.Controls.Add(this.projectPermissionsView);
+      this.permissionsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.permissionsTabPage.Name = "permissionsTabPage";
+      this.permissionsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.permissionsTabPage.Size = new System.Drawing.Size(577, 515);
+      this.permissionsTabPage.TabIndex = 1;
+      this.permissionsTabPage.Text = "Permissions";
+      this.permissionsTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectPermissionsView
+      // 
+      this.projectPermissionsView.Caption = "ProjectView";
+      this.projectPermissionsView.Content = null;
+      this.projectPermissionsView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectPermissionsView.Location = new System.Drawing.Point(3, 3);
+      this.projectPermissionsView.Name = "projectPermissionsView";
+      this.projectPermissionsView.ReadOnly = false;
+      this.projectPermissionsView.Size = new System.Drawing.Size(571, 509);
+      this.projectPermissionsView.TabIndex = 0;
+      // 
+      // resourcesTabPage
+      // 
+      this.resourcesTabPage.Controls.Add(this.projectResourcesView);
+      this.resourcesTabPage.Location = new System.Drawing.Point(4, 22);
+      this.resourcesTabPage.Name = "resourcesTabPage";
+      this.resourcesTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.resourcesTabPage.Size = new System.Drawing.Size(577, 515);
+      this.resourcesTabPage.TabIndex = 2;
+      this.resourcesTabPage.Text = "Resources";
+      this.resourcesTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectResourcesView
+      // 
+      this.projectResourcesView.Caption = "ProjectView";
+      this.projectResourcesView.Content = null;
+      this.projectResourcesView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectResourcesView.Location = new System.Drawing.Point(3, 3);
+      this.projectResourcesView.Name = "projectResourcesView";
+      this.projectResourcesView.ReadOnly = false;
+      this.projectResourcesView.Size = new System.Drawing.Size(571, 509);
+      this.projectResourcesView.TabIndex = 0;
+      //
+      // jobsTabPage
+      //
+      this.jobsTabPage.Controls.Add(this.projectJobsView);
+      this.jobsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.jobsTabPage.Name = "jobsTabPage";
+      this.jobsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.jobsTabPage.Size = new System.Drawing.Size(577, 515);
+      this.jobsTabPage.TabIndex = 3;
+      this.jobsTabPage.Text = "Jobs";
+      this.jobsTabPage.UseVisualStyleBackColor = true;
+      //
+      // projectJobsView
+      //
+      this.projectJobsView.Caption = "ProjectView";
+      this.projectJobsView.Content = null;
+      this.projectJobsView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectJobsView.Location = new System.Drawing.Point(3, 3);
+      this.projectJobsView.Name = "projectJobsView";
+      this.projectJobsView.ReadOnly = false;
+      this.projectJobsView.Size = new System.Drawing.Size(571, 509);
+      this.projectJobsView.TabIndex = 0;
+      // 
+      // ProjectsView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Name = "ProjectsView";
+      this.Size = new System.Drawing.Size(853, 553);
+      this.Load += new System.EventHandler(this.ProjectsView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.tabControl.ResumeLayout(false);
+      this.detailsTabPage.ResumeLayout(false);
+      this.permissionsTabPage.ResumeLayout(false);
+      this.resourcesTabPage.ResumeLayout(false);
+      this.jobsTabPage.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button saveProjectButton;
+    private System.Windows.Forms.Button removeButton;
+    private System.Windows.Forms.Button addButton;
+    private System.Windows.Forms.TreeView projectsTreeView;
+    private System.Windows.Forms.TabControl tabControl;
+    private System.Windows.Forms.TabPage detailsTabPage;
+    private System.Windows.Forms.TabPage permissionsTabPage;
+    private ProjectView projectView;
+    private System.Windows.Forms.ImageList imageList;
+    private System.Windows.Forms.TabPage resourcesTabPage;
+    private System.Windows.Forms.TabPage jobsTabPage;
+    private ProjectResourcesView projectResourcesView;
+    private ProjectPermissionsView projectPermissionsView;
+    private ProjectJobsView projectJobsView;
+    private System.Windows.Forms.ToolTip toolTip;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs	(revision 16240)
@@ -0,0 +1,570 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.Collections;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("Projects View")]
+  [Content(typeof(IItemList<Project>), false)]
+  public partial class ProjectsView : ItemView, IDisposable {
+    private const int greenFlagImageIndex = 0;
+    private const int redFlagImageIndex = 1;
+    private const string SELECTED_TAG = ""; // " [selected]";
+    private const string NOT_STORED_TAG = "*"; // " [not stored]";
+    private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]";
+    private const string INACTIVE_TAG = " [inactive]"; 
+
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
+    private Project selectedProject = null;
+    public Project SelectedProject {
+      get { return selectedProject; }
+      set { if (selectedProject != value) ChangeSelectedProject(value); }
+    }
+
+    private readonly object locker = new object();
+
+    public new IItemList<Project> Content {
+      get { return (IItemList<Project>)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectsView() {
+      InitializeComponent();
+
+      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagGreen);
+      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagRed);
+
+      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
+      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
+      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
+      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
+    }
+
+    #region Overrides
+    protected override void OnClosing(FormClosingEventArgs e) {
+      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
+      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
+      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
+      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
+      base.OnClosing(e);
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.ItemsAdded += Content_ItemsAdded;
+      Content.ItemsRemoved += Content_ItemsRemoved;
+    }
+
+    protected override void DeregisterContentEvents() {
+      Content.ItemsRemoved -= Content_ItemsRemoved;
+      Content.ItemsAdded -= Content_ItemsAdded;
+      base.DeregisterContentEvents();
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        projectsTreeView.Nodes.Clear();
+        projectView.Content = null;
+        projectPermissionsView.Content = null;
+        projectResourcesView.Content = null;
+        projectJobsView.Content = null;
+        selectedProject = null;
+      } else {
+        BuildProjectTree(Content);
+      }
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();      
+
+      bool locked = Content == null || Locked || ReadOnly;      
+      bool parentOwner = selectedProject != null && HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id);
+      bool selectedProjectDisabled = selectedProject == null 
+                                     || selectedProject != null && selectedProject.Id == Guid.Empty;
+
+      bool selectedProjectHasJobs =
+        !selectedProjectDisabled && HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id)
+                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null
+                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any();
+
+      bool addLocked = locked
+                       || (selectedProject == null && !IsAdmin())
+                       || (selectedProject != null && selectedProject.Id == Guid.Empty)
+                       || (selectedProject != null && !IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id)
+                       || (selectedProject != null && (DateTime.Now < selectedProject.StartDate || DateTime.Now > selectedProject.EndDate));
+
+      bool deleteLocked = locked
+                          || !Content.Any()
+                          || selectedProject == null
+                          || Content.Any(x => x.ParentProjectId == selectedProject.Id)
+                          || selectedProjectHasJobs
+                          || (!IsAdmin() && !parentOwner);
+
+      bool saveLocked = locked
+                        || !Content.Any()
+                        || selectedProject == null
+                        || (!IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id);
+
+
+      addButton.Enabled = !addLocked;
+      removeButton.Enabled = !deleteLocked;
+      saveProjectButton.Enabled = !saveLocked;
+
+      projectView.Enabled = !locked;
+      projectPermissionsView.Enabled = !locked && !selectedProjectDisabled;
+      projectResourcesView.Enabled = !locked && !selectedProjectDisabled;
+      projectJobsView.Enabled = !locked && !selectedProjectDisabled;
+
+      projectView.Locked = locked;
+      projectPermissionsView.Locked = locked || selectedProjectDisabled;
+      projectResourcesView.Locked = locked || selectedProjectDisabled;
+      projectJobsView.Locked = locked || selectedProjectDisabled;
+    }
+    #endregion
+
+    #region Event Handlers
+    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsAdded, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsRemoved, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void ProjectViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)ProjectViewContent_PropertyChanged, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private async void ProjectsView_Load(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateProjects());
+    }
+
+    private async void refreshButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!refreshButton.Enabled) return;
+        refreshButton.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateProjects(),
+        finallyCallback: () => {
+          refreshButton.Enabled = true;
+        });
+    }
+
+    private void addButton_Click(object sender, EventArgs e) {      
+
+      if (selectedProject == null && !IsAdmin()) {
+        MessageBox.Show(
+          "You are not allowed to add a root project - please select a parent project.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+      
+      if (selectedProject != null && selectedProject.Id == Guid.Empty) {
+        MessageBox.Show(
+          "You cannot add a project to a not yet stored project.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+
+      var project = new Project {
+        Name = "New Project",
+        OwnerUserId = UserInformation.Instance.User.Id,        
+      };
+      if(selectedProject != null) {
+        project.ParentProjectId = selectedProject.Id;
+        project.EndDate = selectedProject.EndDate;
+      }
+
+      SelectedProject = project;
+      Content.Add(project);
+    }
+
+    private async void removeButton_Click(object sender, EventArgs e) {
+      if (selectedProject == null) return;
+
+      lock (locker) {
+        // for details go to ChangeSelectedProject(..)
+        if (!removeButton.Enabled) return;
+        removeButton.Enabled = false;
+      }
+
+      var result = MessageBox.Show(
+        "Do you really want to delete " + selectedProject.Name + "?",
+        "HeuristicLab Hive Administrator",
+        MessageBoxButtons.YesNo,
+        MessageBoxIcon.Question);
+
+      if (result == DialogResult.Yes) {
+        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+          action: () => {
+            RemoveProject(selectedProject);  
+          });
+      }
+      SetEnabledStateOfControls();
+    }
+
+    private async void saveProjectButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!saveProjectButton.Enabled) return;
+        saveProjectButton.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          //if (selectedProject != null && selectedProject.Id == Guid.Empty)
+          //  SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(selectedProject.Id).LastOrDefault();
+          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
+          foreach (var project in projectsToSave)
+            project.Store();
+          
+          UpdateProjects();
+        },
+        finallyCallback: () => saveProjectButton.Enabled = true);
+
+      OnContentChanged();
+    }
+
+    private void projectsTreeView_MouseDown(object sender, MouseEventArgs e) {
+      var node = projectsTreeView.GetNodeAt(e.Location);
+      if (node == null) return;
+      var p = (Project)node.Tag;
+      if(!HiveAdminClient.Instance.DisabledParentProjects.Contains(p)) ChangeSelectedProjectNode(node);
+    }
+
+    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      e.Cancel = true;
+    }
+
+    private void projectsTreeView_DragDrop(object sender, DragEventArgs e) {
+      if (e.Effect == DragDropEffects.None) return;
+
+      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
+      if (sourceNode == null) return;
+      var sourceProject = ((Project)sourceNode.Tag);
+      if (sourceProject == null) return;
+
+      var treeView = (TreeView)sender;
+      if (sourceNode.TreeView != treeView) return;
+
+      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
+      var targetNode = treeView.GetNodeAt(targetPoint);
+
+      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
+
+      if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
+        MessageBox.Show(
+          "You cannot drag projects to this project.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+
+      if (sourceNode.Parent == null) {
+        treeView.Nodes.Remove(sourceNode);
+      } else {
+        sourceNode.Parent.Nodes.Remove(sourceNode);
+        sourceProject.ParentProjectId = null;
+      }
+
+      if (targetNode == null) {
+        treeView.Nodes.Add(sourceNode);
+      } else if(targetProject.Id != Guid.Empty) {
+        targetNode.Nodes.Add(sourceNode);
+        sourceProject.ParentProjectId = targetProject.Id;
+      }
+
+      SelectedProject = sourceProject;
+      OnContentChanged();
+    }
+
+    private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
+      var sourceNode = (TreeNode)e.Item;
+      if (IsAuthorized((Project)sourceNode.Tag))
+        DoDragDrop(sourceNode, DragDropEffects.All);
+    }
+
+    private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.Move;
+
+      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
+      var sourceProject = ((Project)sourceNode.Tag);
+
+      var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y));
+      var targetNode = projectsTreeView.GetNodeAt(targetPoint);
+      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
+
+      if ((!IsAdmin() && (targetNode == null || targetProject == null))
+      || sourceNode == null
+      || sourceProject == null
+      || sourceNode == targetNode
+      || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
+        e.Effect = DragDropEffects.None;
+      }
+    }
+
+    private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
+      e.Action = DragAction.Continue;
+    }
+    #endregion
+
+    #region Helpers
+    private void BuildProjectTree(IEnumerable<Project> projects) {
+      projectsTreeView.Nodes.Clear();
+      if (!projects.Any()) return;
+
+      var disabledParentProjects = HiveAdminClient.Instance.DisabledParentProjects;
+      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
+      //var parentedMainProjects = new HashSet<Project>(projects
+      //  .Where(x => x.ParentProjectId.HasValue
+      //  && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)));
+      //mainProjects.UnionWith(parentedMainProjects);
+      var mainDisabledParentProjects = new HashSet<Project>(disabledParentProjects.Where(x => x.ParentProjectId == null || x.ParentProjectId == Guid.Empty));
+      mainProjects.UnionWith(mainDisabledParentProjects);
+      var subProbjects = new HashSet<Project>(projects.Union(disabledParentProjects).Except(mainProjects));
+
+      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
+      if (selectedProject != null) SelectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault();
+      bool nodeSelected = false;
+
+      TreeNode currentNode = null;
+      Project currentProject = null;
+
+      while (stack.Any()) {
+        var newProject = stack.Pop();
+        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
+        StyleTreeNode(newNode, newProject);
+      
+        if (selectedProject == null && !disabledParentProjects.Contains(newProject)) {
+          SelectedProject = newProject;
+        }
+        if (!nodeSelected && selectedProject != null && selectedProject.Id == newProject.Id) {
+          newNode.BackColor = selectedBackColor;
+          newNode.ForeColor = selectedForeColor;
+          newNode.Text += SELECTED_TAG;
+          nodeSelected = true;
+        }
+
+        // search for parent node of newNode and save in currentNode
+        // necessary since newNodes (stack top items) might be siblings
+        // or grand..grandparents of previous node (currentNode)
+        while (currentNode != null && newProject.ParentProjectId != currentProject.Id) {
+          currentNode = currentNode.Parent;
+          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          projectsTreeView.Nodes.Add(newNode);
+          newNode.ImageIndex = greenFlagImageIndex;
+        } else {
+          currentNode.Nodes.Add(newNode);
+          newNode.ImageIndex = redFlagImageIndex;
+        }
+
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+
+        if (disabledParentProjects.Contains(newProject)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        }
+
+        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
+        if (childProjects.Any()) {
+          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
+            subProbjects.Remove(project);
+            stack.Push(project);
+          }
+          currentNode = newNode;
+          currentProject = newProject;
+        }
+      }
+
+      projectsTreeView.ExpandAll();
+    }
+
+    private void StyleTreeNode(TreeNode n, Project p) {
+      n.Text = p.Name;
+      n.BackColor = Color.Transparent;
+      n.ForeColor = Color.Black;
+
+      if (HiveAdminClient.Instance.DisabledParentProjects.Select(x => x.Id).Contains(p.Id)) {
+        n.ForeColor = grayTextColor;
+      } else {
+        if (p.Id == Guid.Empty) {
+          n.Text += NOT_STORED_TAG;
+        } else if (p.Modified) {
+          n.Text += CHANGES_NOT_STORED_TAG;
+        }
+
+        if (!IsActive(p)) {
+          n.ForeColor = grayTextColor;
+          n.Text += INACTIVE_TAG;
+        }
+      }
+    }
+
+    private void ResetTreeNodes(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        StyleTreeNode(n, (Project)n.Tag);
+        if (n.Nodes.Count > 0) {
+          ResetTreeNodes(n.Nodes);
+        }
+      }
+    }
+
+    private void ChangeSelectedProject(Project project) {
+      projectView.Content = project;
+      projectPermissionsView.Content = project;
+      projectResourcesView.Content = project;
+      projectJobsView.Content = project;
+      selectedProject = project;
+      SetEnabledStateOfControls();
+    }
+
+    private void ChangeSelectedProjectNode(TreeNode projectNode) {
+      if (projectNode == null) return;
+      SelectedProject = (Project)projectNode.Tag;
+      ResetTreeNodes(projectsTreeView.Nodes);
+      projectNode.BackColor = selectedBackColor;
+      projectNode.ForeColor = selectedForeColor;
+      projectNode.Text += SELECTED_TAG;
+    }
+
+    private void UpdateProjects() {
+      try {
+        HiveAdminClient.Instance.Refresh();
+        Content = HiveAdminClient.Instance.Projects;
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void RemoveProject(Project project) {
+      if (project == null) return;
+
+      try {
+        if (project.Id != Guid.Empty) {
+          SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(project.Id).LastOrDefault();
+          HiveAdminClient.Delete(project);          
+          UpdateProjects();
+        } else {          
+          SelectedProject = Content.FirstOrDefault(x => x.Id == project.ParentProjectId);
+          Content.Remove(project);
+        }
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private bool IsActive(Project project) {
+      return DateTime.Now >= project.StartDate
+             && (project.EndDate == null
+                 || DateTime.Now < project.EndDate.Value);
+    }
+
+    private bool IsAuthorized(Project project) {
+      return project != null && UserInformation.Instance.UserExists;
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.Designer.cs	(revision 16240)
@@ -48,8 +48,6 @@
       this.chbade = new System.Windows.Forms.CheckBox();
       this.dtpEnd = new System.Windows.Forms.DateTimePicker();
-      this.dtpToTime = new System.Windows.Forms.DateTimePicker();
       this.label5 = new System.Windows.Forms.Label();
       this.dtpStart = new System.Windows.Forms.DateTimePicker();
-      this.dtpFromTime = new System.Windows.Forms.DateTimePicker();
       this.label6 = new System.Windows.Forms.Label();
       this.btSaveRecurrence = new System.Windows.Forms.Button();
@@ -74,8 +72,6 @@
       this.gbAppointment.Controls.Add(this.chbade);
       this.gbAppointment.Controls.Add(this.dtpEnd);
-      this.gbAppointment.Controls.Add(this.dtpToTime);
       this.gbAppointment.Controls.Add(this.label5);
       this.gbAppointment.Controls.Add(this.dtpStart);
-      this.gbAppointment.Controls.Add(this.dtpFromTime);
       this.gbAppointment.Controls.Add(this.label6);
       this.gbAppointment.Location = new System.Drawing.Point(2, 2);
@@ -98,19 +94,10 @@
       // dtpEnd
       // 
-      this.dtpEnd.Format = System.Windows.Forms.DateTimePickerFormat.Short;
+      this.dtpEnd.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpEnd.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpEnd.Location = new System.Drawing.Point(113, 56);
       this.dtpEnd.Name = "dtpEnd";
-      this.dtpEnd.Size = new System.Drawing.Size(91, 20);
+      this.dtpEnd.Size = new System.Drawing.Size(175, 20);
       this.dtpEnd.TabIndex = 29;
-      // 
-      // dtpToTime
-      // 
-      this.dtpToTime.CustomFormat = "HH:mm";
-      this.dtpToTime.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.dtpToTime.Location = new System.Drawing.Point(226, 56);
-      this.dtpToTime.Name = "dtpToTime";
-      this.dtpToTime.ShowUpDown = true;
-      this.dtpToTime.Size = new System.Drawing.Size(73, 20);
-      this.dtpToTime.TabIndex = 38;
       // 
       // label5
@@ -126,18 +113,9 @@
       // 
       this.dtpStart.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpStart.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpStart.Location = new System.Drawing.Point(113, 19);
       this.dtpStart.Name = "dtpStart";
-      this.dtpStart.Size = new System.Drawing.Size(89, 20);
+      this.dtpStart.Size = new System.Drawing.Size(175, 20);
       this.dtpStart.TabIndex = 28;
-      // 
-      // dtpFromTime
-      // 
-      this.dtpFromTime.CustomFormat = "HH:mm";
-      this.dtpFromTime.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.dtpFromTime.Location = new System.Drawing.Point(226, 19);
-      this.dtpFromTime.Name = "dtpFromTime";
-      this.dtpFromTime.ShowUpDown = true;
-      this.dtpFromTime.Size = new System.Drawing.Size(73, 20);
-      this.dtpFromTime.TabIndex = 30;
       // 
       // label6
@@ -307,6 +285,4 @@
     private System.Windows.Forms.Button btSaveRecurrence;
     private System.Windows.Forms.Button btCancelRecurrence;
-    private System.Windows.Forms.DateTimePicker dtpFromTime;
-    private System.Windows.Forms.DateTimePicker dtpToTime;
     private System.Windows.Forms.CheckBox chbade;
     private System.Windows.Forms.GroupBox gbWeekly;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.cs	(revision 16240)
@@ -46,6 +46,6 @@
       //check if valid
       if (InputIsValid()) {
-        dateFrom = DateTime.Parse(dtpStart.Text + " " + dtpFromTime.Text);
-        dateTo = DateTime.Parse(dtpEnd.Text + " " + dtpToTime.Text);
+        dateFrom = DateTime.Parse(dtpStart.Text);
+        dateTo = DateTime.Parse(dtpEnd.Text);
 
         RecurrentEvent recurrentEvent = new RecurrentEvent() {
@@ -89,6 +89,6 @@
       DateTime dateFrom, dateTo;
 
-      dateFrom = DateTime.Parse(dtpStart.Text + " " + dtpFromTime.Text);
-      dateTo = DateTime.Parse(dtpEnd.Text + " " + dtpToTime.Text);
+      dateFrom = DateTime.Parse(dtpStart.Text);
+      dateTo = DateTime.Parse(dtpEnd.Text);
 
       if (chbade.Checked && dateFrom < dateTo) {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs	(revision 16240)
@@ -0,0 +1,171 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ResourceView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.idLabel = new System.Windows.Forms.Label();
+      this.idTextBox = new System.Windows.Forms.TextBox();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.descriptionLabel = new System.Windows.Forms.Label();
+      this.descriptionTextBox = new System.Windows.Forms.TextBox();
+      this.heartbeatIntervalLabel = new System.Windows.Forms.Label();
+      this.heartbeatIntervalNumericUpDown = new Hive.Views.Extensions.FixedNumericUpDown();
+      this.publicLabel = new System.Windows.Forms.Label();
+      this.publicCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // idLabel
+      // 
+      this.idLabel.AutoSize = true;
+      this.idLabel.Location = new System.Drawing.Point(3, 11);
+      this.idLabel.Name = "idLabel";
+      this.idLabel.Size = new System.Drawing.Size(19, 13);
+      this.idLabel.TabIndex = 0;
+      this.idLabel.Text = "Id:";
+      // 
+      // idTextBox
+      // 
+      this.idTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.idTextBox.Location = new System.Drawing.Point(130, 8);
+      this.idTextBox.Name = "idTextBox";
+      this.idTextBox.ReadOnly = true;
+      this.idTextBox.Size = new System.Drawing.Size(397, 20);
+      this.idTextBox.TabIndex = 1;
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(3, 37);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 2;
+      this.nameLabel.Text = "Name:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(130, 34);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.Size = new System.Drawing.Size(397, 20);
+      this.nameTextBox.TabIndex = 3;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
+      // 
+      // descriptionLabel
+      // 
+      this.descriptionLabel.AutoSize = true;
+      this.descriptionLabel.Location = new System.Drawing.Point(3, 63);
+      this.descriptionLabel.Name = "descriptionLabel";
+      this.descriptionLabel.Size = new System.Drawing.Size(63, 13);
+      this.descriptionLabel.TabIndex = 4;
+      this.descriptionLabel.Text = "Description:";
+      // 
+      // descriptionTextBox
+      // 
+      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.descriptionTextBox.Location = new System.Drawing.Point(130, 60);
+      this.descriptionTextBox.Name = "descriptionTextBox";
+      this.descriptionTextBox.Size = new System.Drawing.Size(397, 20);
+      this.descriptionTextBox.TabIndex = 5;
+      this.descriptionTextBox.TextChanged += new System.EventHandler(this.descriptionTextBox_TextChanged);
+      // 
+      // heartbeatIntervalLabel
+      // 
+      this.heartbeatIntervalLabel.AutoSize = true;
+      this.heartbeatIntervalLabel.Location = new System.Drawing.Point(3, 89);
+      this.heartbeatIntervalLabel.Name = "heartbeatIntervalLabel";
+      this.heartbeatIntervalLabel.Size = new System.Drawing.Size(121, 13);
+      this.heartbeatIntervalLabel.TabIndex = 8;
+      this.heartbeatIntervalLabel.Text = "Heartbeat Interval [sec]:";
+      // 
+      // heartbeatIntervalNumericUpDown
+      // 
+      this.heartbeatIntervalNumericUpDown.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.heartbeatIntervalNumericUpDown.Location = new System.Drawing.Point(130, 86);
+      this.heartbeatIntervalNumericUpDown.Name = "heartbeatIntervalNumericUpDown";
+      this.heartbeatIntervalNumericUpDown.Size = new System.Drawing.Size(397, 20);
+      this.heartbeatIntervalNumericUpDown.TabIndex = 9;
+      this.heartbeatIntervalNumericUpDown.Minimum = 0;
+      this.heartbeatIntervalNumericUpDown.Maximum = 120;
+      this.heartbeatIntervalNumericUpDown.ValueChanged += new System.EventHandler(this.heartbeatIntervalNumericUpDown_ValueChanged);
+      // 
+      // publicLabel
+      // 
+      this.publicLabel.AutoSize = true;
+      this.publicLabel.Location = new System.Drawing.Point(3, 115);
+      this.publicLabel.Name = "publicLabel";
+      this.publicLabel.Size = new System.Drawing.Size(39, 13);
+      this.publicLabel.TabIndex = 12;
+      this.publicLabel.Text = "Public:";
+      // 
+      // publicCheckBox
+      // 
+      this.publicCheckBox.AutoSize = true;
+      this.publicCheckBox.Location = new System.Drawing.Point(130, 114);
+      this.publicCheckBox.Name = "publicCheckBox";
+      this.publicCheckBox.Size = new System.Drawing.Size(15, 14);
+      this.publicCheckBox.TabIndex = 13;
+      this.publicCheckBox.UseVisualStyleBackColor = true;
+      this.publicCheckBox.CheckedChanged += new System.EventHandler(this.publicCheckBox_CheckedChanged);
+      // 
+      // SlaveGroupView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.publicCheckBox);
+      this.Controls.Add(this.publicLabel);
+      this.Controls.Add(this.heartbeatIntervalNumericUpDown);
+      this.Controls.Add(this.heartbeatIntervalLabel);
+      this.Controls.Add(this.descriptionTextBox);
+      this.Controls.Add(this.descriptionLabel);
+      this.Controls.Add(this.nameTextBox);
+      this.Controls.Add(this.nameLabel);
+      this.Controls.Add(this.idTextBox);
+      this.Controls.Add(this.idLabel);
+      this.Name = "SlaveGroupView";
+      this.Size = new System.Drawing.Size(530, 350);
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).EndInit();
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Label idLabel;
+    private System.Windows.Forms.Label nameLabel;
+    private System.Windows.Forms.Label descriptionLabel;
+    private System.Windows.Forms.Label heartbeatIntervalLabel;
+    private System.Windows.Forms.Label publicLabel;
+    protected System.Windows.Forms.TextBox nameTextBox;
+    protected System.Windows.Forms.TextBox idTextBox;
+    protected System.Windows.Forms.TextBox descriptionTextBox;
+    protected HeuristicLab.Clients.Hive.Views.Extensions.FixedNumericUpDown heartbeatIntervalNumericUpDown;
+    protected System.Windows.Forms.CheckBox publicCheckBox;
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs	(revision 16240)
@@ -0,0 +1,92 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("Resource View")]
+  [Content(typeof(Resource), IsDefaultView = true)]
+  public partial class ResourceView : ItemView {
+    public new Resource Content {
+      get { return (Resource)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ResourceView() {
+      InitializeComponent();
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        idTextBox.Clear();
+        nameTextBox.Clear();
+        descriptionTextBox.Clear();
+        heartbeatIntervalNumericUpDown.Value = 0;
+        publicCheckBox.Checked = false;
+      } else {
+        idTextBox.Text = Content.Id.ToString();
+        nameTextBox.Text = Content.Name;
+        descriptionTextBox.Text = Content.Description;
+        heartbeatIntervalNumericUpDown.Value = Content.HbInterval;
+        publicCheckBox.Checked = Content.OwnerUserId == null;
+      }
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      idTextBox.ReadOnly = true;
+      nameTextBox.ReadOnly = !enabled;
+      descriptionTextBox.ReadOnly = !enabled;
+      heartbeatIntervalNumericUpDown.ReadOnly = !enabled;
+      publicCheckBox.Enabled = enabled;
+    }
+    #endregion
+
+    #region Event Handlers
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != nameTextBox.Text)
+        Content.Name = nameTextBox.Text;
+    }
+
+    private void descriptionTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Description != descriptionTextBox.Text)
+        Content.Description = descriptionTextBox.Text;
+    }
+
+    private void heartbeatIntervalNumericUpDown_ValueChanged(object sender, EventArgs e) {
+      if (Content != null && Content.HbInterval != (int)heartbeatIntervalNumericUpDown.Value)
+        Content.HbInterval = (int)heartbeatIntervalNumericUpDown.Value;
+    }
+
+    private void publicCheckBox_CheckedChanged(object sender, EventArgs e) {
+      var newOwnerUserId = publicCheckBox.Checked ? null : (Guid?)UserInformation.Instance.User.Id;
+      if (Content != null && Content.OwnerUserId != newOwnerUserId)
+        Content.OwnerUserId = newOwnerUserId;
+    }
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 16240)
@@ -37,9 +37,4 @@
         components.Dispose();
       }
-      HiveAdminClient.Instance.Refreshing -= new EventHandler(Instance_Refreshing);
-      HiveAdminClient.Instance.Refreshed -= new EventHandler(Instance_Refreshed);
-
-      Access.AccessClient.Instance.Refreshing -= new EventHandler(AccessClient_Refreshing);
-      Access.AccessClient.Instance.Refreshed -= new EventHandler(AccessClient_Refreshed);
       base.Dispose(disposing);
     }
@@ -56,18 +51,14 @@
       this.splitSlaves = new System.Windows.Forms.SplitContainer();
       this.btnRefresh = new System.Windows.Forms.Button();
-      this.progressBar = new System.Windows.Forms.ProgressBar();
       this.btnSave = new System.Windows.Forms.Button();
       this.btnRemoveGroup = new System.Windows.Forms.Button();
       this.btnAddGroup = new System.Windows.Forms.Button();
-      this.btnPermissionsSave = new System.Windows.Forms.Button();
-      this.treeSlaveGroup = new System.Windows.Forms.TreeView();
+      this.treeView = new Hive.Views.TreeView.NoDoubleClickTreeView();
       this.tabSlaveGroup = new System.Windows.Forms.TabControl();
       this.tabDetails = new System.Windows.Forms.TabPage();
-      this.slaveView = new HeuristicLab.Clients.Hive.Administrator.Views.SlaveView();
+      this.viewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
       this.tabSchedule = new System.Windows.Forms.TabPage();
       this.scheduleView = new HeuristicLab.Clients.Hive.Administrator.Views.ScheduleView();
       this.toolTip = new System.Windows.Forms.ToolTip(this.components);
-      this.tabPermissions = new System.Windows.Forms.TabPage();
-      this.permissionView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
       ((System.ComponentModel.ISupportInitialize)(this.splitSlaves)).BeginInit();
       this.splitSlaves.Panel1.SuspendLayout();
@@ -77,5 +68,4 @@
       this.tabDetails.SuspendLayout();
       this.tabSchedule.SuspendLayout();
-      this.tabPermissions.SuspendLayout();
       this.SuspendLayout();
       // 
@@ -88,6 +78,6 @@
       // splitSlaves
       // 
-      this.splitSlaves.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.splitSlaves.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.splitSlaves.Location = new System.Drawing.Point(3, 3);
@@ -97,9 +87,8 @@
       // 
       this.splitSlaves.Panel1.Controls.Add(this.btnRefresh);
-      this.splitSlaves.Panel1.Controls.Add(this.progressBar);
       this.splitSlaves.Panel1.Controls.Add(this.btnSave);
       this.splitSlaves.Panel1.Controls.Add(this.btnRemoveGroup);
       this.splitSlaves.Panel1.Controls.Add(this.btnAddGroup);
-      this.splitSlaves.Panel1.Controls.Add(this.treeSlaveGroup);
+      this.splitSlaves.Panel1.Controls.Add(this.treeView);
       // 
       // splitSlaves.Panel2
@@ -117,24 +106,16 @@
       this.btnRefresh.Size = new System.Drawing.Size(24, 24);
       this.btnRefresh.TabIndex = 8;
-      this.toolTip.SetToolTip(this.btnRefresh, "Fetch list from server");
+      this.toolTip.SetToolTip(this.btnRefresh, "Fetch list from server.");
       this.btnRefresh.UseVisualStyleBackColor = true;
       this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
       // 
-      // progressBar
-      // 
-      this.progressBar.Location = new System.Drawing.Point(123, 4);
-      this.progressBar.Name = "progressBar";
-      this.progressBar.Size = new System.Drawing.Size(123, 23);
-      this.progressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
-      this.progressBar.TabIndex = 7;
-      // 
       // btnSave
       // 
-      this.btnSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.btnSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.btnSave.Location = new System.Drawing.Point(93, 3);
       this.btnSave.Name = "btnSave";
       this.btnSave.Size = new System.Drawing.Size(24, 24);
       this.btnSave.TabIndex = 5;
-      this.toolTip.SetToolTip(this.btnSave, "Store slave and group configuration on the server");
+      this.toolTip.SetToolTip(this.btnSave, "Store slave and group configuration on the server.");
       this.btnSave.UseVisualStyleBackColor = true;
       this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
@@ -147,5 +128,5 @@
       this.btnRemoveGroup.Size = new System.Drawing.Size(24, 24);
       this.btnRemoveGroup.TabIndex = 2;
-      this.toolTip.SetToolTip(this.btnRemoveGroup, "Delete a slave or a group");
+      this.toolTip.SetToolTip(this.btnRemoveGroup, "Remove a slave or a group.");
       this.btnRemoveGroup.UseVisualStyleBackColor = true;
       this.btnRemoveGroup.Click += new System.EventHandler(this.btnRemoveGroup_Click);
@@ -158,36 +139,38 @@
       this.btnAddGroup.Size = new System.Drawing.Size(24, 24);
       this.btnAddGroup.TabIndex = 1;
-      this.toolTip.SetToolTip(this.btnAddGroup, "Add a new group");
+      this.toolTip.SetToolTip(this.btnAddGroup, "Add a new group.");
       this.btnAddGroup.UseVisualStyleBackColor = true;
       this.btnAddGroup.Click += new System.EventHandler(this.btnAddGroup_Click);
       // 
-      // treeSlaveGroup
-      // 
-      this.treeSlaveGroup.AllowDrop = true;
-      this.treeSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.treeSlaveGroup.ImageIndex = 0;
-      this.treeSlaveGroup.ImageList = this.imageListSlaveGroups;
-      this.treeSlaveGroup.Location = new System.Drawing.Point(3, 33);
-      this.treeSlaveGroup.Name = "treeSlaveGroup";
-      this.treeSlaveGroup.SelectedImageIndex = 0;
-      this.treeSlaveGroup.Size = new System.Drawing.Size(243, 511);
-      this.treeSlaveGroup.TabIndex = 0;
-      this.treeSlaveGroup.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeSlaveGroup_ItemDrag);
-      this.treeSlaveGroup.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeSlaveGroup_AfterSelect);
-      this.treeSlaveGroup.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragDrop);
-      this.treeSlaveGroup.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnter);
-      this.treeSlaveGroup.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragOver);
-      this.treeSlaveGroup.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.treeSlaveGroup_QueryContinueDrag);
+      // treeView
+      // 
+      this.treeView.AllowDrop = true;
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageListSlaveGroups;
+      this.treeView.Location = new System.Drawing.Point(3, 33);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(243, 511);
+      this.treeView.TabIndex = 0;
+      this.treeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeSlaveGroup_ItemDrag);
+      this.treeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeSlaveGroup_MouseDown);
+      this.treeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeSlaveGroup_BeforeSelect);
+      this.treeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragDrop);
+      this.treeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnterOver);
+      this.treeView.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnterOver);
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeSlaveGroup_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeSlaveGroup_AfterCheck);
       // 
       // tabSlaveGroup
       // 
-      this.tabSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.tabSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.tabSlaveGroup.Controls.Add(this.tabDetails);
       this.tabSlaveGroup.Controls.Add(this.tabSchedule);
-      this.tabSlaveGroup.Controls.Add(this.tabPermissions);
       this.tabSlaveGroup.Location = new System.Drawing.Point(3, 3);
       this.tabSlaveGroup.Name = "tabSlaveGroup";
@@ -195,9 +178,10 @@
       this.tabSlaveGroup.Size = new System.Drawing.Size(585, 541);
       this.tabSlaveGroup.TabIndex = 1;
-      this.tabSlaveGroup.SelectedIndexChanged += new System.EventHandler(this.tabSlaveGroup_SelectedIndexChanged);
+      //this.tabSlaveGroup.TabIndexChanged += TabSlaveGroup_TabIndexChanged;
+      this.tabSlaveGroup.Selected += TabSlaveGroup_Selected;
       // 
       // tabDetails
       // 
-      this.tabDetails.Controls.Add(this.slaveView);
+      this.tabDetails.Controls.Add(this.viewHost);
       this.tabDetails.Location = new System.Drawing.Point(4, 22);
       this.tabDetails.Name = "tabDetails";
@@ -208,16 +192,19 @@
       this.tabDetails.UseVisualStyleBackColor = true;
       // 
-      // slaveView
-      // 
-      this.slaveView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.slaveView.Caption = "SlaveView";
-      this.slaveView.Content = null;
-      this.slaveView.Location = new System.Drawing.Point(6, 6);
-      this.slaveView.Name = "slaveView";
-      this.slaveView.ReadOnly = false;
-      this.slaveView.Size = new System.Drawing.Size(565, 503);
-      this.slaveView.TabIndex = 0;
+      // viewHost
+      // 
+      this.viewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.viewHost.Caption = "View";
+      this.viewHost.Content = null;
+      this.viewHost.Enabled = true;
+      this.viewHost.Location = new System.Drawing.Point(6, 6);
+      this.viewHost.Name = "viewHost";
+      this.viewHost.ReadOnly = false;
+      this.viewHost.Size = new System.Drawing.Size(565, 503);
+      this.viewHost.TabIndex = 0;
+      this.viewHost.ViewsLabelVisible = true;
+      this.viewHost.ViewType = null;
       // 
       // tabSchedule
@@ -234,6 +221,6 @@
       // scheduleView
       // 
-      this.scheduleView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.scheduleView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.scheduleView.Caption = "ScheduleView";
@@ -244,42 +231,4 @@
       this.scheduleView.Size = new System.Drawing.Size(565, 503);
       this.scheduleView.TabIndex = 0;
-      // 
-      // tabPermissions
-      // 
-      this.tabPermissions.Controls.Add(this.btnPermissionsSave);
-      this.tabPermissions.Controls.Add(this.permissionView);
-      this.tabPermissions.Location = new System.Drawing.Point(4, 22);
-      this.tabPermissions.Name = "tabPermissions";
-      this.tabPermissions.Padding = new System.Windows.Forms.Padding(3);
-      this.tabPermissions.Size = new System.Drawing.Size(577, 515);
-      this.tabPermissions.TabIndex = 2;
-      this.tabPermissions.Text = "Permissions";
-      this.tabPermissions.UseVisualStyleBackColor = true;
-      // 
-      // btnPermissionsSave
-      // 
-      this.btnPermissionsSave.Enabled = false;
-      this.btnPermissionsSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
-      this.btnPermissionsSave.Location = new System.Drawing.Point(39, 9);
-      this.btnPermissionsSave.Name = "btnPermissionsSave";
-      this.btnPermissionsSave.Size = new System.Drawing.Size(24, 24);
-      this.btnPermissionsSave.TabIndex = 1;
-      this.toolTip.SetToolTip(this.btnPermissionsSave, "Store slave and group sharing permissions on the server");
-      this.btnPermissionsSave.UseVisualStyleBackColor = true;
-      this.btnPermissionsSave.Click += new System.EventHandler(this.btnPermissionsSave_Click);
-      // 
-      // permissionView
-      // 
-      this.permissionView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.permissionView.Caption = "RefreshableLightweightUser View";
-      this.permissionView.Content = null;
-      this.permissionView.FetchSelectedUsers = null;
-      this.permissionView.Location = new System.Drawing.Point(6, 6);
-      this.permissionView.Name = "permissionView";
-      this.permissionView.ReadOnly = false;
-      this.permissionView.Size = new System.Drawing.Size(565, 503);
-      this.permissionView.TabIndex = 0;
       // 
       // ResourcesView
@@ -297,9 +246,7 @@
       this.tabDetails.ResumeLayout(false);
       this.tabSchedule.ResumeLayout(false);
-      this.tabPermissions.ResumeLayout(false);
       this.ResumeLayout(false);
 
     }
-
     #endregion
 
@@ -307,18 +254,14 @@
     private System.Windows.Forms.Button btnRemoveGroup;
     private System.Windows.Forms.Button btnAddGroup;
-    private System.Windows.Forms.TreeView treeSlaveGroup;
+    private HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView treeView;
     private System.Windows.Forms.TabControl tabSlaveGroup;
     private System.Windows.Forms.TabPage tabDetails;
     private System.Windows.Forms.TabPage tabSchedule;
-    private SlaveView slaveView;
     private System.Windows.Forms.ImageList imageListSlaveGroups;
     private ScheduleView scheduleView;
     private System.Windows.Forms.Button btnSave;
-    private System.Windows.Forms.ProgressBar progressBar;
     private System.Windows.Forms.Button btnRefresh;
     private System.Windows.Forms.ToolTip toolTip;
-    private System.Windows.Forms.TabPage tabPermissions;
-    private Access.Views.RefreshableLightweightUserView permissionView;
-    private System.Windows.Forms.Button btnPermissionsSave;
+    private MainForm.WindowsForms.ViewHost viewHost;
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 16240)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -22,16 +22,15 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Drawing;
 using System.Linq;
-using System.ServiceModel.Security;
-using System.Threading;
-using System.Threading.Tasks;
 using System.Windows.Forms;
 using HeuristicLab.Clients.Access;
 using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.Collections;
+using HeuristicLab.Common.Resources;
 using HeuristicLab.Core;
 using HeuristicLab.Core.Views;
 using HeuristicLab.MainForm;
-using TS = System.Threading.Tasks;
 
 namespace HeuristicLab.Clients.Hive.Administrator.Views {
@@ -39,4 +38,31 @@
   [Content(typeof(IItemList<Resource>), false)]
   public partial class ResourcesView : ItemView, IDisposable {
+    private const int slaveImageIndex = 0;
+    private const int slaveGroupImageIndex = 1;
+    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
+    public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
+    private const string SELECTED_TAG = ""; // " [selected]";
+    private const string NOT_STORED_TAG = "*"; // " [not stored]";
+    private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]";
+
+    private readonly Color changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color calculatingColor = Color.FromArgb(255, 58, 114, 35); // #3a7223
+    private readonly Color offlineColor = Color.FromArgb(255, 187, 36, 36); // #bb2424
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
+
+
+    private TreeNode ungroupedGroupNode;
+
+    private Resource selectedResource = null;
+    public Resource SelectedResource {
+      get { return selectedResource; }
+      set { if (selectedResource != value) ChangeSelectedResource(value); }
+    }
+
+    private readonly object locker = new object();
+
     public new IItemList<Resource> Content {
       get { return (IItemList<Resource>)base.Content; }
@@ -44,223 +70,475 @@
     }
 
-    public const string UngroupedGroupName = "UNGROUPED";
-    private const int slaveImageIndex = 0;
-    private const int slaveGroupImageIndex = 1;
-    private readonly Color ownedResourceColor = Color.LightGreen;
-    private TS.Task progressTask;
-    private bool stopProgressTask;
-    private bool currentlyAuthorized;
-
-
     public ResourcesView() {
       InitializeComponent();
-      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.MonitorLarge);
-      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NetworkCenterLarge);
-
-      HiveAdminClient.Instance.Refreshing += new EventHandler(Instance_Refreshing);
-      HiveAdminClient.Instance.Refreshed += new EventHandler(Instance_Refreshed);
-
-      Access.AccessClient.Instance.Refreshing += new EventHandler(AccessClient_Refreshing);
-      Access.AccessClient.Instance.Refreshed += new EventHandler(AccessClient_Refreshed);
-    }
-
-    private void UpdateProgress() {
-      while (!stopProgressTask) {
-        int diff = (progressBar.Maximum - progressBar.Minimum) / 10;
-
-        if (progressBar.InvokeRequired) {
-          progressBar.Invoke(new Action(delegate() { progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum; }));
-        } else {
-          progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum;
-        }
-
-        //ok, this is not very clever...
-        Thread.Sleep(500);
-      }
-      if (progressBar.InvokeRequired) {
-        progressBar.Invoke(new Action(delegate() { progressBar.Value = progressBar.Minimum; }));
-      } else {
-        progressBar.Value = progressBar.Minimum;
-      }
-    }
-
-    void Instance_Refreshing(object sender, EventArgs e) {
-      stopProgressTask = false;
-      progressTask = new TS.Task(UpdateProgress);
-      progressTask.Start();
-      SetEnabledStateOfControls();
-    }
-
-    void Instance_Refreshed(object sender, EventArgs e) {
-      stopProgressTask = true;
-      SetEnabledStateOfControls();
-    }
-
-    void AccessClient_Refreshing(object sender, EventArgs e) {
-      stopProgressTask = false;
-      progressTask = new TS.Task(UpdateProgress);
-      progressTask.Start();
-      SetEnabledStateOfControls();
-      btnPermissionsSave.Enabled = false;
-    }
-
-    void AccessClient_Refreshed(object sender, EventArgs e) {
-      stopProgressTask = true;
-      SetEnabledStateOfControls();
-    }
-
-    #region Register Content Events
-    protected override void DeregisterContentEvents() {
-      Content.ItemsAdded -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
-      Content.ItemsRemoved -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
-      base.DeregisterContentEvents();
-    }
+
+      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
+      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
+
+      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
+      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
+      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
+      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
+    }
+
+    #region Overrides
+    protected override void OnClosing(FormClosingEventArgs e) {
+      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
+      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
+      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
+      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
+      base.OnClosing(e);
+    }
+
     protected override void RegisterContentEvents() {
       base.RegisterContentEvents();
-      Content.ItemsAdded += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
-      Content.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
-    }
-    #endregion
+      Content.ItemsAdded += Content_ItemsAdded;
+      Content.ItemsRemoved += Content_ItemsRemoved;
+    }
+
+    protected override void DeregisterContentEvents() {
+      Content.ItemsRemoved -= Content_ItemsRemoved;
+      Content.ItemsAdded -= Content_ItemsAdded;
+      base.DeregisterContentEvents();
+    }
 
     protected override void OnContentChanged() {
       base.OnContentChanged();
       if (Content == null) {
-        slaveView.Content = null;
+        treeView.Nodes.Clear();
+        viewHost.Content = null;
         scheduleView.Content = null;
-        permissionView.Content = null;
-        permissionView.FetchSelectedUsers = null;
-        treeSlaveGroup.Nodes.Clear();
       } else {
-        permissionView.Content = Access.AccessClient.Instance;
-        treeSlaveGroup.Nodes.Clear();
-
-        //rebuild
-        TreeNode ungrp = new TreeNode(UngroupedGroupName);
-        ungrp.ImageIndex = slaveGroupImageIndex;
-        ungrp.SelectedImageIndex = ungrp.ImageIndex;
-        var newGroup = new SlaveGroup();
-        newGroup.Name = UngroupedGroupName;
-        newGroup.Id = Guid.NewGuid();
-        newGroup.Description = "Contains slaves which are in no group";
-        ungrp.Tag = newGroup;
-
-        foreach (Resource g in Content) {
-          if (g.GetType() == typeof(SlaveGroup)) {
-            //root node
-            if (g.ParentResourceId == null) {
-              TreeNode tn = new TreeNode();
-              tn.ImageIndex = slaveGroupImageIndex;
-              tn.SelectedImageIndex = tn.ImageIndex;
-
-              tn.Tag = g;
-              tn.Text = g.Name;
-              if (g.OwnerUserId == Access.UserInformation.Instance.User.Id) tn.BackColor = ownedResourceColor;
-
-              BuildSlaveGroupTree(g, tn);
-              treeSlaveGroup.Nodes.Add(tn);
-            }
-          } else if (g.GetType() == typeof(Slave)) {
-            if (g.ParentResourceId == null) {
-              var stn = new TreeNode(g.Name);
-              stn.ImageIndex = slaveImageIndex;
-              stn.SelectedImageIndex = stn.ImageIndex;
-              stn.Tag = g;
-              if (g.OwnerUserId == Access.UserInformation.Instance.User.Id) stn.BackColor = ownedResourceColor;
-              ungrp.Nodes.Add(stn);
-            }
-          }
-        }
-        treeSlaveGroup.Nodes.Add(ungrp);
-      }
-    }
-
-    private void BuildSlaveGroupTree(Resource g, TreeNode tn) {
-      foreach (Resource r in Content.Where(s => s.ParentResourceId != null && s.ParentResourceId == g.Id)) {
-        TreeNode stn = new TreeNode(r.Name);
-        if (r is Slave) {
-          stn.ImageIndex = slaveImageIndex;
-        } else if (r is SlaveGroup) {
-          stn.ImageIndex = slaveGroupImageIndex;
-        }
-        stn.SelectedImageIndex = stn.ImageIndex;
-        stn.Tag = r;
-        if (r.OwnerUserId == Access.UserInformation.Instance.User.Id) stn.BackColor = ownedResourceColor;
-        tn.Nodes.Add(stn);
-
-        BuildSlaveGroupTree(r, stn);
-      }
+        BuildResourceTree(Content);
+      }
+      SetEnabledStateOfControls();
     }
 
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-      if (Content == null) {
-        btnAddGroup.Enabled = false;
+
+      bool locked = Content == null || Locked || ReadOnly;
+      bool addLocked = locked
+                    || !IsAdmin()
+                    || (selectedResource is Slave && selectedResource.ParentResourceId != null)
+                    || (selectedResource != null && selectedResource.Id == Guid.Empty);
+
+      HashSet<Guid> descendantResources = null;
+      bool selectedRDeleteLocked = selectedResource == null
+                              || (selectedResource.Id != Guid.Empty && (!HiveAdminClient.Instance.ResourceDescendants.TryGetValue(selectedResource.Id, out descendantResources) || descendantResources.Any()));
+
+      var nodes = GetCheckedNodes(treeView.Nodes).ToList();
+      var checkedResources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+      bool checkedRDeleteLocked = false;
+      for (int i = 0; !checkedRDeleteLocked && i < checkedResources.Count; i++) {
+        if (checkedResources[i].Id != Guid.Empty &&
+            (!HiveAdminClient.Instance.ResourceDescendants.TryGetValue(checkedResources[i].Id, out descendantResources) ||
+             descendantResources.Any()))
+          checkedRDeleteLocked = true;
+      }
+
+      bool deleteLocked = locked
+                          || !IsAdmin()
+                          || !Content.Any()
+                          || checkedResources.Any() && checkedRDeleteLocked
+                          || !checkedResources.Any() && selectedRDeleteLocked;
+
+      bool saveLocked = locked
+                       || !IsAdmin()
+                       || !Content.Any()
+                       || selectedResource == null;
+
+      btnAddGroup.Enabled = !addLocked;
+      btnRemoveGroup.Enabled = !deleteLocked;
+      btnSave.Enabled = !saveLocked;
+      viewHost.Locked = locked || !IsAdmin();
+      scheduleView.Locked = locked || !IsAdmin();
+    }
+    #endregion
+
+    #region Event Handlers
+    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsAdded, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsRemoved, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void SlaveViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)SlaveViewContent_PropertyChanged, sender, e);
+      else {
+        OnContentChanged();
+        if (e.PropertyName == "HbInterval") {
+          UpdateChildHbIntervall((Resource)viewHost.Content);
+        }
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private async void ResourcesView_Load(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateResources());
+    }
+
+    private async void btnRefresh_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!btnRefresh.Enabled) return;
+        btnRefresh.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateResources(),
+        finallyCallback: () => btnRefresh.Enabled = true);
+    }
+
+    private void btnAddGroup_Click(object sender, EventArgs e) {
+      var parentResourceId = selectedResource is SlaveGroup ? selectedResource.Id : (Guid?)null;
+
+      var group = new SlaveGroup {
+        Name = "New Group",
+        OwnerUserId = UserInformation.Instance.User.Id,
+        ParentResourceId = parentResourceId
+      };
+
+      SelectedResource = group;
+      Content.Add(group);
+    }
+
+    private async void btnRemoveGroup_Click(object sender, EventArgs e) {      
+      var nodes = GetCheckedNodes(treeView.Nodes).ToList();
+      var checkedResources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+      if (selectedResource == null && !checkedResources.Any()) return;
+
+      lock (locker) {
+        if (!btnRemoveGroup.Enabled) return;
         btnRemoveGroup.Enabled = false;
+      }
+
+      if (checkedResources.Count > 0) {
+        var result = MessageBox.Show(
+          "Do you really want to delete all " + checkedResources.Count + " checked resources?",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+        if (result == DialogResult.Yes) {
+          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+            action: () => {
+              RemoveResource(checkedResources);
+            });
+        }
+      } else {
+        var res = checkedResources.Any() ? checkedResources.First() : selectedResource;
+        var result = MessageBox.Show(
+          "Do you really want to delete the selected resource " + res.Name + "?",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+        if (result == DialogResult.Yes) {
+          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+            action: () => {
+              RemoveResource(res);
+            });
+        }
+      }
+
+      OnContentChanged();
+      SetEnabledStateOfControls();
+    }
+
+    private async void btnSave_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!btnSave.Enabled) return;
         btnSave.Enabled = false;
-        btnPermissionsSave.Enabled = false;
-        permissionView.Enabled = false;
-        scheduleView.SetEnabledStateOfSchedule(false);
-        btnPermissionsSave.Enabled = false;
-        permissionView.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          var resourcesToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
+          foreach (var resource in resourcesToSave)
+            resource.Store();
+          UpdateResources();
+        });
+
+      OnContentChanged();
+      SetEnabledStateOfControls();
+    }
+
+    private void treeSlaveGroup_MouseDown(object sender, MouseEventArgs e) {
+      var node = treeView.GetNodeAt(e.Location);
+      if (node == null || node == ungroupedGroupNode) return;
+      var r = (Resource)node.Tag;
+      if (!HiveAdminClient.Instance.DisabledParentResources.Contains(r)) ChangeSelectedResourceNode(node);
+    }
+
+    private void treeSlaveGroup_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      e.Cancel = true;
+    }
+
+    private void treeSlaveGroup_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      if (!IsAdmin() || e.Node == ungroupedGroupNode) {
+        e.Cancel = true;
       } else {
-        btnAddGroup.Enabled = true;
-        btnRemoveGroup.Enabled = true;
-        btnSave.Enabled = true;
-        scheduleView.SetEnabledStateOfSchedule(IsAuthorized(slaveView.Content));
-        btnPermissionsSave.Enabled = permissionView.FetchSelectedUsers != null;
-        permissionView.Enabled = permissionView.FetchSelectedUsers != null;
-      }
-    }
-
-    private bool IsAuthorized(Resource resource) {
-      return resource != null
-          && resource.Name != UngroupedGroupName
-          && resource.Id != Guid.Empty
-          && UserInformation.Instance.UserExists
-          && (resource.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions());
-    }
-
-    private void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) {
-      if (e.Action != TreeViewAction.Unknown) {
-        Resource selectedResource = ((Resource)e.Node.Tag);
-        currentlyAuthorized = IsAuthorized(selectedResource);
-        if (currentlyAuthorized) {
-          permissionView.FetchSelectedUsers = new Func<List<Guid>>(() => {
-            return HiveServiceLocator.Instance.CallHiveService<List<ResourcePermission>>(service => {
-              return service.GetResourcePermissions(selectedResource.Id);
-            }).Select(x => x.GrantedUserId).ToList();
-          });
-          if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
+        var r = (Resource)e.Node.Tag;
+        if (HiveAdminClient.Instance.DisabledParentResources.Contains(r)) {
+          e.Cancel = true;
+        }
+      }
+    }
+
+    private void treeSlaveGroup_AfterCheck(object sender, TreeViewEventArgs e) {
+      SetEnabledStateOfControls();
+    }
+
+    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
+      if (e.Effect == DragDropEffects.None) return;
+
+      var targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
+      var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null;
+      var resources = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as IEnumerable<Resource>;
+
+      foreach (var r in resources) {
+        r.ParentResourceId = targetResource != null ? targetResource.Id : (Guid?)null;
+      }
+
+      // TODO
+      //HiveAdminClient.Instance.UpdateResourceGenealogy(Content);
+      OnContentChanged();
+    }
+
+    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
+      if (!IsAdmin()) return;
+
+      var nodes = GetCheckedNodes(treeView.Nodes).ToList();
+      TreeNode sourceNode = (TreeNode)e.Item;
+      if (!sourceNode.Checked) nodes.Add(sourceNode);
+      nodes.Remove(ungroupedGroupNode);
+      ungroupedGroupNode.Checked = false;
+      var resources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+
+      if (resources.Count > 0) {
+        DataObject data = new DataObject();
+        data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, resources);
+        var action = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
+        if (action.HasFlag(DragDropEffects.Move)) {
+          foreach (var node in nodes) node.Remove();
+          StyleTreeNode(ungroupedGroupNode, (Resource)ungroupedGroupNode.Tag, resources);
+        }
+      }
+    }
+
+    private IEnumerable<TreeNode> GetCheckedNodes(TreeNodeCollection nodes) {
+      if (nodes != null) {
+        foreach (var node in nodes.OfType<TreeNode>()) {
+          if (node.Checked && node != ungroupedGroupNode) yield return node;
+          foreach (var child in GetCheckedNodes(node.Nodes))
+            yield return child;
+        }
+      }
+    }
+
+    private void treeSlaveGroup_DragEnterOver(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.Move;
+      var resources = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as IEnumerable<Resource>;
+      var targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
+      var targetResource = (targetNode != null ? targetNode.Tag : null) as Resource;
+
+      if (!IsAdmin()
+        || resources == null
+        || !resources.Any()
+        || resources.Any(x => !HiveAdminClient.Instance.CheckParentChange(x, targetResource))
+        || (targetNode != null && (targetNode == ungroupedGroupNode || targetNode.Parent == ungroupedGroupNode))) {
+        e.Effect = DragDropEffects.None;
+      }
+    }
+
+    private void TabSlaveGroup_TabIndexChanged(object sender, EventArgs e) {
+      throw new NotImplementedException();
+    }
+
+    private async void TabSlaveGroup_Selected(object sender, System.Windows.Forms.TabControlEventArgs e) {
+      if (e.TabPage == tabSchedule) {
+        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+          action: () => UpdateSchedule(),
+          finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
+      }
+      SetEnabledStateOfControls();
+    }
+    #endregion
+
+    #region Helpers
+    private void BuildResourceTree(IEnumerable<Resource> resources) {
+      treeView.Nodes.Clear();
+      if (!resources.Any()) return;
+
+      var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
+      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+        .Where(x => x.ParentResourceId == null));
+      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
+      //mainResources.UnionWith(parentedMainResources);
+      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
+      mainResources.UnionWith(mainDisabledParentResources);
+      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
+
+      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
+      if (selectedResource != null) SelectedResource = resources.Where(x => x.Id == selectedResource.Id).FirstOrDefault();
+      bool nodeSelected = false;
+
+      TreeNode currentNode = null;
+      Resource currentResource = null;
+
+      while (stack.Any()) {
+        var newResource = stack.Pop();
+        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
+        StyleTreeNode(newNode, newResource, resources);
+
+        if (selectedResource == null && !disabledParentResources.Contains(newResource)) {
+          SelectedResource = newResource;
+        }
+        if (!nodeSelected && selectedResource != null && newResource.Id == selectedResource.Id) {
+          newNode.BackColor = selectedBackColor;
+          newNode.ForeColor = selectedForeColor;
+          newNode.Text += SELECTED_TAG;
+          nodeSelected = true;
+        }
+
+        if (disabledParentResources.Contains(newResource)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        }
+
+        // search for parent node of newNode and save in currentNode
+        // necessary since newNodes (stack top items) might be siblings
+        // or grand..grandparents of previous node (currentNode)
+        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
+          currentNode = currentNode.Parent;
+          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          treeView.Nodes.Add(newNode);
         } else {
-          permissionView.FetchSelectedUsers = null;
-          btnPermissionsSave.Enabled = false;
-          if (selectedResource.Id == Guid.Empty) {
-            if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
-          } else tabSlaveGroup.TabPages.Remove(tabPermissions);
-        }
-
-        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
-          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
-        }
-
-        slaveView.Content = selectedResource;
-        HiveAdminClient.Instance.DowntimeForResourceId = selectedResource.Id;
-
-        if (selectedResource is SlaveGroup) {
-          slaveView.Content.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
-        }
-
-        if (tabSlaveGroup.SelectedIndex == 1) {
-          UpdateScheduleAsync();
-        } else if (tabSlaveGroup.SelectedIndex == 2) {
-          UpdatePermissionsAsync();
-        }
-      }
-    }
-
-    void SlaveViewContent_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
-      OnContentChanged();
-      if (e.PropertyName == "HbInterval") {
-        UpdateChildHbIntervall(slaveView.Content);
+          currentNode.Nodes.Add(newNode);
+        }
+
+        if (newResource is SlaveGroup) {
+          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
+          if (childResources.Any()) {
+            foreach (var resource in childResources.OrderByDescending(x => x.Name)) {
+              subResources.Remove(resource);
+              stack.Push(resource);
+            }
+            currentNode = newNode;
+            currentResource = newResource;
+          }
+        }
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+      }
+
+      // collapse slave-only nodes
+      foreach (TreeNode n in treeView.Nodes) {
+        CollapseSlaveOnlyNodes(n);
+      }
+
+      ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
+        ForeColor = SystemColors.GrayText,
+        ImageIndex = slaveGroupImageIndex,
+        Tag = new SlaveGroup() {
+          Name = UNGROUPED_GROUP_NAME,
+          Description = UNGROUPED_GROUP_DESCRIPTION
+        }
+      };
+
+      foreach (var slave in subResources.OfType<Slave>().OrderBy(x => x.Name)) {
+        var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+        StyleTreeNode(slaveNode, slave, resources);
+        ungroupedGroupNode.Nodes.Add(slaveNode);
+        if (selectedResource == null) {
+          SelectedResource = slave;
+        }
+
+        if (slave.Id == selectedResource.Id && !nodeSelected) {
+          slaveNode.BackColor = selectedBackColor;
+          slaveNode.ForeColor = selectedForeColor;
+          slaveNode.Text += SELECTED_TAG;
+          nodeSelected = true;
+        }
+      }
+
+      if (ungroupedGroupNode.Nodes.Count > 0) {
+        ungroupedGroupNode.Text += " [" + ungroupedGroupNode.Nodes.Count.ToString() + "]";
+        ungroupedGroupNode.Expand();
+        treeView.Nodes.Add(ungroupedGroupNode);
+      }
+    }
+
+    private void CollapseSlaveOnlyNodes(TreeNode tn) {
+      Resource r = (Resource)tn.Tag;
+      var descendants = GetResourceDescendants();
+      if (descendants.ContainsKey(r.Id)) {
+        if (descendants[r.Id].OfType<SlaveGroup>().Any()) {
+          tn.Expand();
+          foreach (TreeNode n in tn.Nodes) CollapseSlaveOnlyNodes(n);
+        } else {
+          tn.Collapse();
+        }
+      }
+    }
+
+    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        Resource r = (Resource)n.Tag;
+        if (n.Nodes.Count > 0) {
+          if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
+            n.Expand();
+            ExpandResourceNodesOfInterest(n.Nodes);
+          } else {
+            n.Collapse();
+          }
+        } else {
+          n.Collapse();
+        }
       }
     }
@@ -275,259 +553,221 @@
     }
 
-    private void btnAddGroup_Click(object sender, EventArgs e) {
-      SlaveGroup newGroup = new SlaveGroup();
-      newGroup.Name = "New Group";
-      newGroup.OwnerUserId = UserInformation.Instance.User.Id;
-      Content.Add(newGroup);
-    }
-
-    void Content_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
-      OnContentChanged();
-    }
-
-    void Content_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
-      OnContentChanged();
-    }
-
-    private void btnRemoveGroup_Click(object sender, EventArgs e) {
-      if (treeSlaveGroup.SelectedNode != null && treeSlaveGroup.SelectedNode.Tag != null) {
-        Resource res = (Resource)treeSlaveGroup.SelectedNode.Tag;
-
-        DialogResult diagRes = MessageBox.Show("Do you really want to delete " + res.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
-        if (diagRes == DialogResult.Yes) {
-          if (res is Slave) {
-            Content.Remove(res);
-            HiveAdminClient.Delete(res);
-          } else if (res is SlaveGroup) {
-            //only delete empty groups
-            if (Content.Where(s => s.ParentResourceId == res.Id).Count() < 1) {
-              Content.Remove(res);
-              HiveAdminClient.Delete(res);
-            } else {
-              MessageBox.Show("Only empty groups can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            }
+    private void UpdateResources() {
+      try {
+        HiveAdminClient.Instance.Refresh();
+        Content = HiveAdminClient.Instance.Resources;
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void RemoveResource(Resource resource) {
+      if (resource == null) return;
+
+      try {
+        if (resource.Id != Guid.Empty) {
+          SelectedResource = HiveAdminClient.Instance.GetAvailableResourceAncestors(resource.Id).LastOrDefault();
+
+          // deal with all new, but not yet saved resources
+          var newResources = Content.Where(x => x.ParentResourceId == resource.Id).ToList();
+          if (newResources.Any(x => x.Id != Guid.Empty)) return;
+          foreach (var nr in newResources) Content.Remove(nr);
+
+          HiveAdminClient.Delete(resource);          
+          UpdateResources();
+        } else {
+          SelectedResource = Content.FirstOrDefault(x => x.Id == resource.ParentResourceId);
+          Content.Remove(resource);
+        }
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void RemoveResource(IEnumerable<Resource> resources) {
+      if (resources == null || !resources.Any()) return;
+
+      var ids = resources.Select(x => x.Id).ToList();      
+      try {
+        bool update = false;
+        foreach (var r in resources) {          
+          if (r.Id != Guid.Empty)  {
+            if(r.Id == SelectedResource.Id)
+              SelectedResource = HiveAdminClient.Instance.GetAvailableResourceAncestors(r.Id).LastOrDefault();
+
+            // deal with all new, but not yet saved resources
+            var newResources = Content.Where(x => x.ParentResourceId == r.Id).ToList();
+            if (newResources.Any(x => x.Id != Guid.Empty)) return;
+            foreach (var nr in newResources) Content.Remove(nr);
+
+            HiveAdminClient.Delete(r);
+            update = true;
+          } else {
+            if (r.Id == SelectedResource.Id)
+              SelectedResource = Content.FirstOrDefault(x => x.Id == r.ParentResourceId);
+            Content.Remove(r);
           }
         }
-      }
-    }
-
-    private void btnSave_Click(object sender, EventArgs e) {
-      foreach (Resource res in Content) {
-        if (res is SlaveGroup && res.Id == Guid.Empty) {
-          SlaveGroup slaveGroup = (SlaveGroup)res;
-          slaveGroup.Store();
-        } else if (res.Id != Guid.Empty && res.Modified) {
-          res.Store();
-        }
-      }
-    }
-
-    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
-      if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false)) {
-        Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
-        TreeNode destNode = ((TreeView)sender).GetNodeAt(pt);
-        TreeNode newNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
-
-        if (destNode.TreeView == newNode.TreeView) {
-          if (destNode.Text == UngroupedGroupName || (destNode.Parent != null && destNode.Parent.Text == UngroupedGroupName)) {
-            MessageBox.Show(string.Format("You can't drag items to the group \"{0}\".{1}This group only contains slaves which haven't yet been assigned to a real group.",
-              UngroupedGroupName, Environment.NewLine), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information);
-            return;
-          }
-
-          SlaveGroup sgrp = null;
-          TreeNode parentNode = null;
-          if (destNode.Tag != null && destNode.Tag is SlaveGroup) {
-            sgrp = (SlaveGroup)destNode.Tag;
-            parentNode = destNode;
-          } else if (destNode.Parent != null && destNode.Parent.Tag is SlaveGroup) {
-            sgrp = (SlaveGroup)destNode.Parent.Tag;
-            parentNode = destNode.Parent;
-          }
-
-          if (newNode.Tag is SlaveGroup && CheckParentsEqualsMovedNode(parentNode, newNode)) {
-            return;
-          }
-
-          SlaveGroup parent = (SlaveGroup)parentNode.Tag;
-
-          if (parent.OwnerUserId != null && !IsAuthorized(parent)) {
-            MessageBox.Show(string.Format("You don't have the permissions to drag items to the group \"{0}\".", ((Resource)parentNode.Tag).Name),
-              "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            return;
-          }
-
-          if (sgrp != null && newNode.Tag != null) {
-            //save parent group to get an id
-            if (sgrp.Id == Guid.Empty) {
-              sgrp.Store();
-            }
-
-            if (newNode.Tag is Slave) {
-              Slave slave = (Slave)newNode.Tag;
-              if (slave.ParentResourceId == null || (slave.ParentResourceId != null && slave.ParentResourceId != sgrp.Id)) {
-                slave.ParentResourceId = sgrp.Id;
-                newNode.Remove();
-                parentNode.Nodes.Clear();
-                BuildSlaveGroupTree(sgrp, parentNode);
-              }
-            } else if (newNode.Tag is SlaveGroup) {
-              SlaveGroup slaveGroup = (SlaveGroup)newNode.Tag;
-              if (slaveGroup.ParentResourceId == null || (slaveGroup.ParentResourceId != null && slaveGroup.ParentResourceId != sgrp.Id)) {
-                slaveGroup.ParentResourceId = sgrp.Id;
-                newNode.Remove();
-                parentNode.Nodes.Clear();
-                BuildSlaveGroupTree(sgrp, parentNode);
-              }
-            }
-          }
-        }
-      }
-    }
-
-    private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) {
-      TreeNode tmp = dest;
-
-      while (tmp != null) {
-        if (tmp == movedNode) {
-          return true;
-        }
-        tmp = tmp.Parent;
-      }
-      return false;
-    }
-
-    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
-      TreeNode sourceNode = (TreeNode)e.Item;
-      if (IsAuthorized((Resource)sourceNode.Tag))
-        DoDragDrop(sourceNode, DragDropEffects.All);
-    }
-
-    private void treeSlaveGroup_DragEnter(object sender, DragEventArgs e) {
-      e.Effect = DragDropEffects.Move;
-    }
-
-    private void treeSlaveGroup_DragOver(object sender, DragEventArgs e) {
-      e.Effect = DragDropEffects.Move;
-    }
-
-    private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
-      e.Action = DragAction.Continue;
-    }
-
-    void ResetView() {
-      if (this.InvokeRequired) {
-        Invoke(new Action(ResetView));
+        if (update) UpdateResources();
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void UpdateSchedule() {
+      try {
+        HiveAdminClient.Instance.RefreshCalendar();
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    private void StyleTreeNode(TreeNode n, Resource r, IEnumerable<Resource> resources) {
+      n.Text = r.Name;
+      n.BackColor = Color.Transparent;
+      n.ForeColor = Color.Black;
+
+      if (HiveAdminClient.Instance.DisabledParentResources.Select(x => x.Id).Contains(r.Id)) {
+        n.ForeColor = grayTextColor;
+      } else if (r.Id == Guid.Empty && n != ungroupedGroupNode /*!r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        // not stored (i.e. new)
+        n.Text += NOT_STORED_TAG;
+      } else if (r.Modified && n != ungroupedGroupNode /*!r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        // changed
+        n.Text += CHANGES_NOT_STORED_TAG;
+      }
+
+      // slave count
+      int childSlavesCount = 0;
+      if (r.Id != Guid.Empty && r is SlaveGroup) {
+        var descendants = GetResourceDescendants();
+        if (descendants.ContainsKey(r.Id)) {
+          childSlavesCount = resources
+            .OfType<Slave>()
+            .Where(x => descendants[r.Id].Select(y => y.Id)
+              .Contains(x.Id))
+            .Count();
+        }
+      } else if (n == ungroupedGroupNode /*|| r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        childSlavesCount = resources
+          .OfType<Slave>()
+          .Where(x => x.ParentResourceId == null
+            || (x.ParentResourceId.HasValue && x.ParentResourceId.Value == Guid.Empty))
+          .Count();
+      }
+      if (childSlavesCount > 0)
+        n.Text += " [" + childSlavesCount.ToString() + "]";
+
+      // slave image index, state, utilization
+      if (r is Slave) {
+        n.ImageIndex = slaveImageIndex;
+        var s = r as Slave;
+        if (s.SlaveState == SlaveState.Calculating) {
+          n.ForeColor = calculatingColor;
+          n.Text += " [" + s.CpuUtilization.ToString("N2") + "%]";
+        } else if (s.SlaveState == SlaveState.Offline) {
+          n.ForeColor = offlineColor;
+          if (s.LastHeartbeat.HasValue)
+            n.Text += " [" + (s.LastHeartbeat != null ? s.LastHeartbeat.Value.ToString("g") : null) + "]";
+        }
       } else {
-        treeSlaveGroup.Nodes.Clear();
-
-        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
-          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
-        }
-        slaveView.Content = null;
+        n.ImageIndex = slaveGroupImageIndex;
+      }
+
+      // ungrouped
+      if (n == ungroupedGroupNode /*r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        n.ForeColor = SystemColors.GrayText;
+      }
+    }
+
+    private void ResetTreeNodes(TreeNodeCollection nodes, IEnumerable<Resource> resources) {
+      foreach (TreeNode n in nodes) {
+        StyleTreeNode(n, (Resource)n.Tag, resources);
+        if (n.Nodes.Count > 0) {
+          ResetTreeNodes(n.Nodes, resources);
+        }
+      }
+    }
+
+    private async void ChangeSelectedResource(Resource resource) {
+      selectedResource = resource;
+      viewHost.Content = selectedResource;
+
+      HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty;
+      if (tabSlaveGroup.SelectedTab == tabSchedule) {
+        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+          action: () => UpdateSchedule(),
+          finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
+      }
+
+      SetEnabledStateOfControls();
+    }
+
+    private void ChangeSelectedResourceNode(TreeNode resourceNode) {
+      if (resourceNode == null) return;
+      SelectedResource = (Resource)resourceNode.Tag;
+      ResetTreeNodes(treeView.Nodes, Content);
+      resourceNode.BackColor = selectedBackColor;
+      resourceNode.ForeColor = selectedForeColor;
+      resourceNode.Text += SELECTED_TAG;
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+
+    private void ResetView() {
+      if (InvokeRequired) Invoke((Action)ResetView);
+      else {
+        treeView.Nodes.Clear();
+
+        if (viewHost.Content != null && viewHost.Content is SlaveGroup) {
+          ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged;
+        }
+
+        viewHost.Content = null;
         if (scheduleView.Content != null) {
           scheduleView.Content.Clear();
         }
+
         HiveAdminClient.Instance.ResetDowntime();
       }
     }
 
-    private void UpdateResources() {
-      ResetView();
-
-      try {
-        if (!Access.UserInformation.Instance.UserExists) {
-          //do a refresh just in case that the user has changed his usr and pwd in between
-          Access.UserInformation.Instance.Refresh();
-        }
-        HiveAdminClient.Instance.Refresh();
-        Content = HiveAdminClient.Instance.Resources;
-      }
-      catch (MessageSecurityException) {
-        ShowMessageSecurityException();
-      }
-      catch (AnonymousUserException) {
-        ShowHiveInformationDialog();
-      }
-    }
-
-    private void ShowMessageSecurityException() {
-      if (this.InvokeRequired) {
-        Invoke(new Action(ShowMessageSecurityException));
-      } else {
-        MessageBox.Show("A Message Security error has occured. This normally means that your user name or password is wrong.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-      }
-    }
-
-    private void ShowHiveInformationDialog() {
-      if (this.InvokeRequired) {
-        Invoke(new Action(ShowHiveInformationDialog));
-      } else {
-        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
-          dialog.ShowDialog(this);
-        }
-      }
-    }
-
-    private void UpdateResourcesAsync() {
-      TS.Task.Factory.StartNew(UpdateResources).ContinueWith((t) => {
-        DisplayError(t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
-    }
-
-    private void UpdateSchedule() {
-      HiveAdminClient.Instance.RefreshCalendar();
-      scheduleView.Invoke(new Action(() => {
-        scheduleView.Content = HiveAdminClient.Instance.Downtimes;
-        SetEnabledStateOfControls();
-      }));
-    }
-
-    private void UpdateScheduleAsync() {
-      TS.Task.Factory.StartNew(UpdateSchedule).ContinueWith((t) => {
-        DisplayError(t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
-    }
-
-    private void UpdatePermissions() {
-      if (permissionView.Content != null && permissionView.FetchSelectedUsers != null)
-        permissionView.Invoke(new Action(() => permissionView.ManualRefresh()));
-    }
-
-    private void UpdatePermissionsAsync() {
-      TS.Task.Factory.StartNew(UpdatePermissions).ContinueWith((t) => {
-        DisplayError(t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
-    }
-
-
-    private void DisplayError(Exception ex) {
-      MessageBox.Show(string.Format("An error occured while updating: {0} {1}", Environment.NewLine, ex.Message), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-    }
-
-    private void tabSlaveGroup_SelectedIndexChanged(object sender, EventArgs e) {
-      if (tabSlaveGroup.SelectedIndex == 1) {
-        UpdateScheduleAsync();
-      } else if (tabSlaveGroup.SelectedIndex == 2) {
-        UpdatePermissionsAsync();
-      }
-    }
-
-    private void btnRefresh_Click(object sender, EventArgs e) {
-      UpdateResourcesAsync();
-    }
-
-    private void ResourcesView_Load(object sender, EventArgs e) {
-      UpdateResourcesAsync();
-    }
-
-    private void btnPermissionsSave_Click(object sender, EventArgs e) {
-      SetEnabledStateOfControls();
-      HiveServiceLocator.Instance.CallHiveService(service => {
-        service.GrantResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetAddedUsers().Select(x => x.Id).ToList());
-        service.RevokeResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetDeletedUsers().Select(x => x.Id).ToList());
-      });
-      SetEnabledStateOfControls();
-    }
+
+    private Dictionary<Guid, HashSet<Resource>> GetResourceDescendants() {
+      var resourceDescendants = new Dictionary<Guid, HashSet<Resource>>();
+      //var resources = Content.Where(x => x.Id != Guid.Empty).Union(HiveAdminClient.Instance.DisabledParentResources).ToList();      
+      var resources = Content.Union(HiveAdminClient.Instance.DisabledParentResources).ToList();
+
+      foreach (var r in resources) {
+        if(!resourceDescendants.ContainsKey(r.Id))
+          resourceDescendants.Add(r.Id, new HashSet<Resource>());
+      }
+      foreach (var r in resources) {
+        var parentResourceId = r.ParentResourceId;
+        while (parentResourceId != null) {
+          var parent = resources.SingleOrDefault(x => x.Id == parentResourceId);
+          if (parent != null) {
+            resourceDescendants[parent.Id].Add(r);
+            parentResourceId = parent.ParentResourceId;
+          } else {
+            parentResourceId = null;
+          }
+        }
+      }
+      return resourceDescendants;
+    }
+
+    #endregion
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.Designer.cs	(revision 16240)
@@ -48,6 +48,4 @@
       Calendar.DrawTool drawTool1 = new Calendar.DrawTool();
       this.dvOnline = new Calendar.DayView();
-      this.txttimeTo = new System.Windows.Forms.DateTimePicker();
-      this.txttimeFrom = new System.Windows.Forms.DateTimePicker();
       this.dtpTo = new System.Windows.Forms.DateTimePicker();
       this.groupBox1 = new System.Windows.Forms.GroupBox();
@@ -94,30 +92,11 @@
       this.dvOnline.OnSelectionChanged += new System.EventHandler<System.EventArgs>(this.dvOnline_OnSelectionChanged);
       // 
-      // txttimeTo
-      // 
-      this.txttimeTo.CustomFormat = "HH:mm";
-      this.txttimeTo.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.txttimeTo.Location = new System.Drawing.Point(164, 45);
-      this.txttimeTo.Name = "txttimeTo";
-      this.txttimeTo.ShowUpDown = true;
-      this.txttimeTo.Size = new System.Drawing.Size(73, 20);
-      this.txttimeTo.TabIndex = 40;
-      // 
-      // txttimeFrom
-      // 
-      this.txttimeFrom.CustomFormat = "HH:mm";
-      this.txttimeFrom.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.txttimeFrom.Location = new System.Drawing.Point(164, 12);
-      this.txttimeFrom.Name = "txttimeFrom";
-      this.txttimeFrom.ShowUpDown = true;
-      this.txttimeFrom.Size = new System.Drawing.Size(73, 20);
-      this.txttimeFrom.TabIndex = 39;
-      // 
       // dtpTo
       // 
       this.dtpTo.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpTo.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpTo.Location = new System.Drawing.Point(72, 45);
       this.dtpTo.Name = "dtpTo";
-      this.dtpTo.Size = new System.Drawing.Size(89, 20);
+      this.dtpTo.Size = new System.Drawing.Size(175, 20);
       this.dtpTo.TabIndex = 33;
       // 
@@ -125,6 +104,4 @@
       // 
       this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-      this.groupBox1.Controls.Add(this.txttimeTo);
-      this.groupBox1.Controls.Add(this.txttimeFrom);
       this.groupBox1.Controls.Add(this.dtpTo);
       this.groupBox1.Controls.Add(this.dtpFrom);
@@ -144,7 +121,8 @@
       // 
       this.dtpFrom.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpFrom.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpFrom.Location = new System.Drawing.Point(72, 12);
       this.dtpFrom.Name = "dtpFrom";
-      this.dtpFrom.Size = new System.Drawing.Size(89, 20);
+      this.dtpFrom.Size = new System.Drawing.Size(175, 20);
       this.dtpFrom.TabIndex = 32;
       // 
@@ -157,6 +135,6 @@
       this.chbade.TabIndex = 31;
       this.chbade.Text = "All Day Event";
+      this.chbade.CheckedChanged += new System.EventHandler(this.chbade_CheckedChanged);
       this.chbade.UseVisualStyleBackColor = true;
-      this.chbade.CheckedChanged += new System.EventHandler(this.chbade_CheckedChanged);
       // 
       // btnRecurrence
@@ -220,5 +198,5 @@
       // 
       this.btnSaveCal.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-      this.btnSaveCal.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.btnSaveCal.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.btnSaveCal.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
       this.btnSaveCal.Location = new System.Drawing.Point(640, 41);
@@ -273,7 +251,5 @@
     #endregion
 
-    private System.Windows.Forms.DateTimePicker txttimeTo;
     private Calendar.DayView dvOnline;
-    private System.Windows.Forms.DateTimePicker txttimeFrom;
     private System.Windows.Forms.DateTimePicker dtpTo;
     private System.Windows.Forms.GroupBox groupBox1;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.cs	(revision 16240)
@@ -92,13 +92,10 @@
 
       if (!string.IsNullOrEmpty(dtpFrom.Text) && !string.IsNullOrEmpty(dtpTo.Text)) {
-        if (chbade.Checked) {
+        if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to) {
           //whole day appointment, only dates are visible
-          if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to)
-            offlineTimes.Add(CreateDowntime(from, to.AddDays(1), true, dtType));
-          else
-            MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-        } else if (!string.IsNullOrEmpty(txttimeFrom.Text) && !string.IsNullOrEmpty(txttimeTo.Text)) {
-          //Timeframe appointment
-          if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to) {
+          if (chbade.Checked) {
+            offlineTimes.Add(CreateDowntime(new DateTime(from.Year, from.Month, from.Day), (new DateTime(to.Year, to.Month, to.Day)).AddDays(1), true, dtType));
+          } else {
+            //Timeframe appointment
             if (from.Date == to.Date)
               offlineTimes.Add(CreateDowntime(from, to, false, dtType));
@@ -111,7 +108,11 @@
               offlineTimes.Add(CreateDowntime(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false, dtType));
             }
-          } else
-            MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-        }
+          }
+
+        } else {
+          MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+        }
+
+
         dvOnline.Invalidate();
         return true;
@@ -156,9 +157,12 @@
 
     private void ChangeRecurrenceDowntime(Guid recurringId) {
-      int hourfrom = int.Parse(txttimeFrom.Text.Substring(0, txttimeFrom.Text.IndexOf(':')));
-      int hourTo = int.Parse(txttimeTo.Text.Substring(0, txttimeTo.Text.IndexOf(':')));
-      List<HiveDowntime> recurringDowntimes = offlineTimes.Where(appointment => ((HiveDowntime)appointment).RecurringId == recurringId).ToList();
-      recurringDowntimes.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, hourfrom, 0, 0));
-      recurringDowntimes.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, hourTo, 0, 0));
+      DateTime from, to;
+      if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to) {
+        List<HiveDowntime> recurringDowntimes = offlineTimes.Where(appointment => ((HiveDowntime)appointment).RecurringId == recurringId).ToList();
+        recurringDowntimes.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, from.Hour, 0, 0));
+        recurringDowntimes.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, to.Hour, 0, 0));
+      } else {
+        MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      }
     }
 
@@ -222,16 +226,25 @@
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-    }
-
-    public virtual void SetEnabledStateOfSchedule(bool state) {
-      if (InvokeRequired) {
-        Invoke(new Action(() => SetEnabledStateOfSchedule(state)));
-      } else {
-        if (Content == null) state = false;
-        groupBox1.Enabled = state;
-        btnClearCal.Enabled = state;
-        btnSaveCal.Enabled = state;
-      }
-    }
+      bool enabled = Content != null && !Locked;
+      dtpFrom.Enabled = enabled;
+      dtpTo.Enabled = enabled;
+      chbade.Enabled = enabled;
+      btCreate.Enabled = enabled;
+      btbDelete.Enabled = enabled;      
+      btnRecurrence.Enabled = enabled;
+      btnClearCal.Enabled = enabled;
+      btnSaveCal.Enabled = enabled;
+    }
+
+    //public virtual void SetEnabledStateOfSchedule(bool state) {
+    //  if (InvokeRequired) {
+    //    Invoke(new Action(() => SetEnabledStateOfSchedule(state)));
+    //  } else {
+    //    if (Content == null || Locked || ReadOnly) state = false;
+    //    //groupBox1.Enabled = state;
+    //    btnClearCal.Enabled = state;
+    //    btnSaveCal.Enabled = state;
+    //  }
+    //}
 
     private void btnClearCal_Click(object sender, System.EventArgs e) {
@@ -243,22 +256,20 @@
 
     private void chbade_CheckedChanged(object sender, EventArgs e) {
-      txttimeFrom.Visible = !chbade.Checked;
-      txttimeTo.Visible = !chbade.Checked;
+      if(chbade.Checked) {
+        dtpFrom.Value = new DateTime(dtpFrom.Value.Year, dtpFrom.Value.Month, dtpFrom.Value.Day);
+        dtpTo.Value = new DateTime(dtpTo.Value.Year, dtpTo.Value.Month, dtpTo.Value.Day);
+      }
     }
 
     private void dvOnline_OnSelectionChanged(object sender, EventArgs e) {
       if (dvOnline.Selection == SelectionType.DateRange) {
-        dtpFrom.Text = dvOnline.SelectionStart.ToShortDateString();
-        dtpTo.Text = dvOnline.SelectionEnd.Date.ToShortDateString();
-        txttimeFrom.Text = dvOnline.SelectionStart.ToShortTimeString();
-        txttimeTo.Text = dvOnline.SelectionEnd.ToShortTimeString();
+        dtpFrom.Value = dvOnline.SelectionStart;
+        dtpTo.Value = dvOnline.SelectionEnd.Date;
         btCreate.Text = "Create Downtime";
       }
 
       if (dvOnline.Selection == SelectionType.Appointment) {
-        dtpFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortDateString();
-        dtpTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortDateString();
-        txttimeFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortTimeString();
-        txttimeTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortTimeString();
+        dtpFrom.Value = dvOnline.SelectedAppointment.StartDate;
+        dtpTo.Value = dvOnline.SelectedAppointment.EndDate;
 
         if (dvOnline.SelectedAppointment.Recurring)
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs	(revision 16240)
@@ -45,290 +45,171 @@
     /// </summary>
     private void InitializeComponent() {
-      this.txtSlaveState = new System.Windows.Forms.TextBox();
-      this.txtOS = new System.Windows.Forms.TextBox();
-      this.label15 = new System.Windows.Forms.Label();
-      this.label14 = new System.Windows.Forms.Label();
-      this.txtDetailsDescription = new System.Windows.Forms.TextBox();
-      this.label13 = new System.Windows.Forms.Label();
-      this.txtName = new System.Windows.Forms.TextBox();
-      this.txtCPU = new System.Windows.Forms.TextBox();
-      this.txtMemory = new System.Windows.Forms.TextBox();
-      this.txtLastHeartbeat = new System.Windows.Forms.TextBox();
-      this.label12 = new System.Windows.Forms.Label();
-      this.label11 = new System.Windows.Forms.Label();
-      this.label10 = new System.Windows.Forms.Label();
-      this.label9 = new System.Windows.Forms.Label();
-      this.label1 = new System.Windows.Forms.Label();
-      this.txtFreeMemory = new System.Windows.Forms.TextBox();
-      this.txtId = new System.Windows.Forms.TextBox();
-      this.label2 = new System.Windows.Forms.Label();
-      this.label3 = new System.Windows.Forms.Label();
-      this.txtHbIntervall = new System.Windows.Forms.TextBox();
-      this.cbxDisposable = new System.Windows.Forms.CheckBox();
-      this.label4 = new System.Windows.Forms.Label();
-      this.cbxPublic = new System.Windows.Forms.CheckBox();
-      this.label5 = new System.Windows.Forms.Label();
+      this.cpuLabel = new System.Windows.Forms.Label();
+      this.cpuTextBox = new System.Windows.Forms.TextBox();
+      this.memoryLabel = new System.Windows.Forms.Label();
+      this.memoryTextBox = new System.Windows.Forms.TextBox();
+      this.operatingSystemLabel = new System.Windows.Forms.Label();
+      this.operatingSystemTextBox = new System.Windows.Forms.TextBox();
+      this.stateLabel = new System.Windows.Forms.Label();
+      this.stateTextBox = new System.Windows.Forms.TextBox();
+      this.lastHeartbeatLabel = new System.Windows.Forms.Label();
+      this.lastHeartbeatTextBox = new System.Windows.Forms.TextBox();
+      this.disposableLabel = new System.Windows.Forms.Label();
+      this.disposableCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).BeginInit();
       this.SuspendLayout();
       // 
-      // txtSlaveState
-      // 
-      this.txtSlaveState.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtSlaveState.Enabled = false;
-      this.txtSlaveState.Location = new System.Drawing.Point(146, 242);
-      this.txtSlaveState.Name = "txtSlaveState";
-      this.txtSlaveState.Size = new System.Drawing.Size(390, 20);
-      this.txtSlaveState.TabIndex = 27;
-      // 
-      // txtOS
-      // 
-      this.txtOS.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtOS.Enabled = false;
-      this.txtOS.Location = new System.Drawing.Point(146, 216);
-      this.txtOS.Name = "txtOS";
-      this.txtOS.Size = new System.Drawing.Size(390, 20);
-      this.txtOS.TabIndex = 26;
-      // 
-      // label15
-      // 
-      this.label15.AutoSize = true;
-      this.label15.Location = new System.Drawing.Point(3, 245);
-      this.label15.Name = "label15";
-      this.label15.Size = new System.Drawing.Size(35, 13);
-      this.label15.TabIndex = 25;
-      this.label15.Text = "State:";
-      // 
-      // label14
-      // 
-      this.label14.AutoSize = true;
-      this.label14.Location = new System.Drawing.Point(3, 219);
-      this.label14.Name = "label14";
-      this.label14.Size = new System.Drawing.Size(93, 13);
-      this.label14.TabIndex = 24;
-      this.label14.Text = "Operating System:";
-      // 
-      // txtDetailsDescription
-      // 
-      this.txtDetailsDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtDetailsDescription.Enabled = false;
-      this.txtDetailsDescription.Location = new System.Drawing.Point(146, 112);
-      this.txtDetailsDescription.Name = "txtDetailsDescription";
-      this.txtDetailsDescription.Size = new System.Drawing.Size(390, 20);
-      this.txtDetailsDescription.TabIndex = 23;
-      // 
-      // label13
-      // 
-      this.label13.AutoSize = true;
-      this.label13.Location = new System.Drawing.Point(3, 115);
-      this.label13.Name = "label13";
-      this.label13.Size = new System.Drawing.Size(63, 13);
-      this.label13.TabIndex = 22;
-      this.label13.Text = "Description:";
-      // 
-      // txtName
-      // 
-      this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtName.Location = new System.Drawing.Point(146, 8);
-      this.txtName.Name = "txtName";
-      this.txtName.Size = new System.Drawing.Size(390, 20);
-      this.txtName.TabIndex = 21;
-      this.txtName.TextChanged += new System.EventHandler(this.txtName_TextChanged);
-      // 
-      // txtCPU
-      // 
-      this.txtCPU.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtCPU.Enabled = false;
-      this.txtCPU.Location = new System.Drawing.Point(146, 138);
-      this.txtCPU.Name = "txtCPU";
-      this.txtCPU.Size = new System.Drawing.Size(390, 20);
-      this.txtCPU.TabIndex = 20;
-      // 
-      // txtMemory
-      // 
-      this.txtMemory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtMemory.Enabled = false;
-      this.txtMemory.Location = new System.Drawing.Point(146, 164);
-      this.txtMemory.Name = "txtMemory";
-      this.txtMemory.Size = new System.Drawing.Size(390, 20);
-      this.txtMemory.TabIndex = 19;
-      // 
-      // txtLastHeartbeat
-      // 
-      this.txtLastHeartbeat.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtLastHeartbeat.Enabled = false;
-      this.txtLastHeartbeat.Location = new System.Drawing.Point(146, 268);
-      this.txtLastHeartbeat.Name = "txtLastHeartbeat";
-      this.txtLastHeartbeat.Size = new System.Drawing.Size(390, 20);
-      this.txtLastHeartbeat.TabIndex = 18;
-      // 
-      // label12
-      // 
-      this.label12.AutoSize = true;
-      this.label12.Location = new System.Drawing.Point(3, 271);
-      this.label12.Name = "label12";
-      this.label12.Size = new System.Drawing.Size(80, 13);
-      this.label12.TabIndex = 17;
-      this.label12.Text = "Last Heartbeat:";
-      // 
-      // label11
-      // 
-      this.label11.AutoSize = true;
-      this.label11.Location = new System.Drawing.Point(3, 167);
-      this.label11.Name = "label11";
-      this.label11.Size = new System.Drawing.Size(47, 13);
-      this.label11.TabIndex = 16;
-      this.label11.Text = "Memory:";
-      // 
-      // label10
-      // 
-      this.label10.AutoSize = true;
-      this.label10.Location = new System.Drawing.Point(3, 141);
-      this.label10.Name = "label10";
-      this.label10.Size = new System.Drawing.Size(32, 13);
-      this.label10.TabIndex = 15;
-      this.label10.Text = "CPU:";
-      // 
-      // label9
-      // 
-      this.label9.AutoSize = true;
-      this.label9.Location = new System.Drawing.Point(3, 11);
-      this.label9.Name = "label9";
-      this.label9.Size = new System.Drawing.Size(38, 13);
-      this.label9.TabIndex = 14;
-      this.label9.Text = "Name:";
-      // 
-      // label1
-      // 
-      this.label1.AutoSize = true;
-      this.label1.Location = new System.Drawing.Point(3, 193);
-      this.label1.Name = "label1";
-      this.label1.Size = new System.Drawing.Size(70, 13);
-      this.label1.TabIndex = 28;
-      this.label1.Text = "Free memory:";
-      // 
-      // txtFreeMemory
-      // 
-      this.txtFreeMemory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtFreeMemory.Enabled = false;
-      this.txtFreeMemory.Location = new System.Drawing.Point(146, 190);
-      this.txtFreeMemory.Name = "txtFreeMemory";
-      this.txtFreeMemory.Size = new System.Drawing.Size(390, 20);
-      this.txtFreeMemory.TabIndex = 29;
-      // 
-      // txtId
-      // 
-      this.txtId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtId.Enabled = false;
-      this.txtId.Location = new System.Drawing.Point(146, 86);
-      this.txtId.Name = "txtId";
-      this.txtId.Size = new System.Drawing.Size(390, 20);
-      this.txtId.TabIndex = 30;
-      // 
-      // label2
-      // 
-      this.label2.AutoSize = true;
-      this.label2.Location = new System.Drawing.Point(3, 89);
-      this.label2.Name = "label2";
-      this.label2.Size = new System.Drawing.Size(19, 13);
-      this.label2.TabIndex = 31;
-      this.label2.Text = "Id:";
-      // 
-      // label3
-      // 
-      this.label3.AutoSize = true;
-      this.label3.Location = new System.Drawing.Point(3, 37);
-      this.label3.Name = "label3";
-      this.label3.Size = new System.Drawing.Size(137, 13);
-      this.label3.TabIndex = 32;
-      this.label3.Text = "Heartbeat Intervall (in sec) :";
-      // 
-      // txtHbIntervall
-      // 
-      this.txtHbIntervall.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtHbIntervall.Location = new System.Drawing.Point(146, 34);
-      this.txtHbIntervall.Name = "txtHbIntervall";
-      this.txtHbIntervall.Size = new System.Drawing.Size(390, 20);
-      this.txtHbIntervall.TabIndex = 33;
-      this.txtHbIntervall.TextChanged += new System.EventHandler(this.txtHbIntervall_TextChanged);
-      // 
-      // cbxDisposable
-      // 
-      this.cbxDisposable.AutoSize = true;
-      this.cbxDisposable.Enabled = false;
-      this.cbxDisposable.Location = new System.Drawing.Point(146, 297);
-      this.cbxDisposable.Name = "cbxDisposable";
-      this.cbxDisposable.Size = new System.Drawing.Size(15, 14);
-      this.cbxDisposable.TabIndex = 34;
-      this.cbxDisposable.UseVisualStyleBackColor = true;
-      this.cbxDisposable.CheckedChanged += new System.EventHandler(this.cbxDisposable_CheckedChanged);
-      // 
-      // label4
-      // 
-      this.label4.AutoSize = true;
-      this.label4.Location = new System.Drawing.Point(3, 297);
-      this.label4.Name = "label4";
-      this.label4.Size = new System.Drawing.Size(62, 13);
-      this.label4.TabIndex = 35;
-      this.label4.Text = "Disposable:";
-      // 
-      // cbxPublic
-      // 
-      this.cbxPublic.AutoSize = true;
-      this.cbxPublic.Enabled = false;
-      this.cbxPublic.Location = new System.Drawing.Point(146, 63);
-      this.cbxPublic.Name = "cbxPublic";
-      this.cbxPublic.Size = new System.Drawing.Size(15, 14);
-      this.cbxPublic.TabIndex = 36;
-      this.cbxPublic.UseVisualStyleBackColor = true;
-      this.cbxPublic.CheckedChanged += new System.EventHandler(this.cbxPublic_CheckedChanged);
-      // 
-      // label5
-      // 
-      this.label5.AutoSize = true;
-      this.label5.Location = new System.Drawing.Point(3, 63);
-      this.label5.Name = "label5";
-      this.label5.Size = new System.Drawing.Size(39, 13);
-      this.label5.TabIndex = 37;
-      this.label5.Text = "Public:";
+      // nameTextBox
+      // 
+      this.nameTextBox.ReadOnly = true;
+      // 
+      // cpuLabel
+      // 
+      this.cpuLabel.AutoSize = true;
+      this.cpuLabel.Location = new System.Drawing.Point(3, 141);
+      this.cpuLabel.Name = "cpuLabel";
+      this.cpuLabel.Size = new System.Drawing.Size(32, 13);
+      this.cpuLabel.TabIndex = 16;
+      this.cpuLabel.Text = "CPU:";
+      // 
+      // cpuTextBox
+      // 
+      this.cpuTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.cpuTextBox.Location = new System.Drawing.Point(130, 138);
+      this.cpuTextBox.Name = "cpuTextBox";
+      this.cpuTextBox.ReadOnly = true;
+      this.cpuTextBox.Size = new System.Drawing.Size(397, 20);
+      this.cpuTextBox.TabIndex = 17;
+      // 
+      // memoryLabel
+      // 
+      this.memoryLabel.AutoSize = true;
+      this.memoryLabel.Location = new System.Drawing.Point(3, 167);
+      this.memoryLabel.Name = "memoryLabel";
+      this.memoryLabel.Size = new System.Drawing.Size(47, 13);
+      this.memoryLabel.TabIndex = 18;
+      this.memoryLabel.Text = "Memory:";
+      // 
+      // memoryTextBox
+      // 
+      this.memoryTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.memoryTextBox.Location = new System.Drawing.Point(130, 164);
+      this.memoryTextBox.Name = "memoryTextBox";
+      this.memoryTextBox.ReadOnly = true;
+      this.memoryTextBox.Size = new System.Drawing.Size(397, 20);
+      this.memoryTextBox.TabIndex = 19;
+      // 
+      // operatingSystemLabel
+      // 
+      this.operatingSystemLabel.AutoSize = true;
+      this.operatingSystemLabel.Location = new System.Drawing.Point(3, 193);
+      this.operatingSystemLabel.Name = "operatingSystemLabel";
+      this.operatingSystemLabel.Size = new System.Drawing.Size(93, 13);
+      this.operatingSystemLabel.TabIndex = 20;
+      this.operatingSystemLabel.Text = "Operating System:";
+      // 
+      // operatingSystemTextBox
+      // 
+      this.operatingSystemTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.operatingSystemTextBox.Location = new System.Drawing.Point(130, 190);
+      this.operatingSystemTextBox.Name = "operatingSystemTextBox";
+      this.operatingSystemTextBox.ReadOnly = true;
+      this.operatingSystemTextBox.Size = new System.Drawing.Size(397, 20);
+      this.operatingSystemTextBox.TabIndex = 21;
+      // 
+      // stateLabel
+      // 
+      this.stateLabel.AutoSize = true;
+      this.stateLabel.Location = new System.Drawing.Point(3, 219);
+      this.stateLabel.Name = "stateLabel";
+      this.stateLabel.Size = new System.Drawing.Size(35, 13);
+      this.stateLabel.TabIndex = 22;
+      this.stateLabel.Text = "State:";
+      // 
+      // stateTextBox
+      // 
+      this.stateTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.stateTextBox.Location = new System.Drawing.Point(130, 216);
+      this.stateTextBox.Name = "stateTextBox";
+      this.stateTextBox.ReadOnly = true;
+      this.stateTextBox.Size = new System.Drawing.Size(397, 20);
+      this.stateTextBox.TabIndex = 23;
+      // 
+      // lastHeartbeatLabel
+      // 
+      this.lastHeartbeatLabel.AutoSize = true;
+      this.lastHeartbeatLabel.Location = new System.Drawing.Point(3, 245);
+      this.lastHeartbeatLabel.Name = "lastHeartbeatLabel";
+      this.lastHeartbeatLabel.Size = new System.Drawing.Size(80, 13);
+      this.lastHeartbeatLabel.TabIndex = 24;
+      this.lastHeartbeatLabel.Text = "Last Heartbeat:";
+      // 
+      // lastHeartbeatTextBox
+      // 
+      this.lastHeartbeatTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.lastHeartbeatTextBox.Location = new System.Drawing.Point(130, 242);
+      this.lastHeartbeatTextBox.Name = "lastHeartbeatTextBox";
+      this.lastHeartbeatTextBox.ReadOnly = true;
+      this.lastHeartbeatTextBox.Size = new System.Drawing.Size(397, 20);
+      this.lastHeartbeatTextBox.TabIndex = 25;
+      // 
+      // disposableLabel
+      // 
+      this.disposableLabel.AutoSize = true;
+      this.disposableLabel.Location = new System.Drawing.Point(3, 271);
+      this.disposableLabel.Name = "disposableLabel";
+      this.disposableLabel.Size = new System.Drawing.Size(62, 13);
+      this.disposableLabel.TabIndex = 28;
+      this.disposableLabel.Text = "Disposable:";
+      // 
+      // disposableCheckBox
+      // 
+      this.disposableCheckBox.AutoSize = true;
+      this.disposableCheckBox.Location = new System.Drawing.Point(130, 270);
+      this.disposableCheckBox.Name = "disposableCheckBox";
+      this.disposableCheckBox.Size = new System.Drawing.Size(15, 14);
+      this.disposableCheckBox.TabIndex = 29;
+      this.disposableCheckBox.UseVisualStyleBackColor = true;
+      this.disposableCheckBox.CheckedChanged += new System.EventHandler(this.disposableCheckBox_CheckedChanged);
       // 
       // SlaveView
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
-      this.Controls.Add(this.label5);
-      this.Controls.Add(this.cbxPublic);
-      this.Controls.Add(this.label4);
-      this.Controls.Add(this.cbxDisposable);
-      this.Controls.Add(this.txtHbIntervall);
-      this.Controls.Add(this.label3);
-      this.Controls.Add(this.label2);
-      this.Controls.Add(this.txtId);
-      this.Controls.Add(this.txtFreeMemory);
-      this.Controls.Add(this.label1);
-      this.Controls.Add(this.txtSlaveState);
-      this.Controls.Add(this.txtOS);
-      this.Controls.Add(this.label15);
-      this.Controls.Add(this.label14);
-      this.Controls.Add(this.txtDetailsDescription);
-      this.Controls.Add(this.label13);
-      this.Controls.Add(this.txtName);
-      this.Controls.Add(this.txtCPU);
-      this.Controls.Add(this.txtMemory);
-      this.Controls.Add(this.txtLastHeartbeat);
-      this.Controls.Add(this.label12);
-      this.Controls.Add(this.label11);
-      this.Controls.Add(this.label10);
-      this.Controls.Add(this.label9);
+      this.Controls.Add(this.disposableCheckBox);
+      this.Controls.Add(this.disposableLabel);
+      this.Controls.Add(this.lastHeartbeatTextBox);
+      this.Controls.Add(this.lastHeartbeatLabel);
+      this.Controls.Add(this.stateTextBox);
+      this.Controls.Add(this.stateLabel);
+      this.Controls.Add(this.operatingSystemTextBox);
+      this.Controls.Add(this.operatingSystemLabel);
+      this.Controls.Add(this.memoryTextBox);
+      this.Controls.Add(this.memoryLabel);
+      this.Controls.Add(this.cpuTextBox);
+      this.Controls.Add(this.cpuLabel);
       this.Name = "SlaveView";
-      this.Size = new System.Drawing.Size(539, 407);
+      this.Controls.SetChildIndex(this.idTextBox, 0);
+      this.Controls.SetChildIndex(this.descriptionTextBox, 0);
+      this.Controls.SetChildIndex(this.heartbeatIntervalNumericUpDown, 0);
+      this.Controls.SetChildIndex(this.publicCheckBox, 0);
+      this.Controls.SetChildIndex(this.nameTextBox, 0);
+      this.Controls.SetChildIndex(this.cpuLabel, 0);
+      this.Controls.SetChildIndex(this.cpuTextBox, 0);
+      this.Controls.SetChildIndex(this.memoryLabel, 0);
+      this.Controls.SetChildIndex(this.memoryTextBox, 0);
+      this.Controls.SetChildIndex(this.operatingSystemLabel, 0);
+      this.Controls.SetChildIndex(this.operatingSystemTextBox, 0);
+      this.Controls.SetChildIndex(this.stateLabel, 0);
+      this.Controls.SetChildIndex(this.stateTextBox, 0);
+      this.Controls.SetChildIndex(this.lastHeartbeatLabel, 0);
+      this.Controls.SetChildIndex(this.lastHeartbeatTextBox, 0);
+      this.Controls.SetChildIndex(this.disposableLabel, 0);
+      this.Controls.SetChildIndex(this.disposableCheckBox, 0);
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).EndInit();
       this.ResumeLayout(false);
       this.PerformLayout();
@@ -338,28 +219,16 @@
     #endregion
 
-    private System.Windows.Forms.TextBox txtSlaveState;
-    private System.Windows.Forms.TextBox txtOS;
-    private System.Windows.Forms.Label label15;
-    private System.Windows.Forms.Label label14;
-    private System.Windows.Forms.TextBox txtDetailsDescription;
-    private System.Windows.Forms.Label label13;
-    private System.Windows.Forms.TextBox txtName;
-    private System.Windows.Forms.TextBox txtCPU;
-    private System.Windows.Forms.TextBox txtMemory;
-    private System.Windows.Forms.TextBox txtLastHeartbeat;
-    private System.Windows.Forms.Label label12;
-    private System.Windows.Forms.Label label11;
-    private System.Windows.Forms.Label label10;
-    private System.Windows.Forms.Label label9;
-    private System.Windows.Forms.Label label1;
-    private System.Windows.Forms.TextBox txtFreeMemory;
-    private System.Windows.Forms.TextBox txtId;
-    private System.Windows.Forms.Label label2;
-    private System.Windows.Forms.Label label3;
-    private System.Windows.Forms.TextBox txtHbIntervall;
-    private System.Windows.Forms.CheckBox cbxDisposable;
-    private System.Windows.Forms.Label label4;
-    private System.Windows.Forms.CheckBox cbxPublic;
-    private System.Windows.Forms.Label label5;
+    private System.Windows.Forms.Label cpuLabel;
+    private System.Windows.Forms.TextBox cpuTextBox;
+    private System.Windows.Forms.Label memoryLabel;
+    private System.Windows.Forms.TextBox memoryTextBox;
+    private System.Windows.Forms.Label operatingSystemLabel;
+    private System.Windows.Forms.TextBox operatingSystemTextBox;
+    private System.Windows.Forms.Label stateLabel;
+    private System.Windows.Forms.TextBox stateTextBox;
+    private System.Windows.Forms.Label lastHeartbeatLabel;
+    private System.Windows.Forms.TextBox lastHeartbeatTextBox;
+    private System.Windows.Forms.Label disposableLabel;
+    private System.Windows.Forms.CheckBox disposableCheckBox;
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs	(revision 16240)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -22,15 +22,13 @@
 using System;
 using System.Windows.Forms;
-using HeuristicLab.Clients.Access;
-using HeuristicLab.Core.Views;
 using HeuristicLab.MainForm;
 using HeuristicLab.MainForm.WindowsForms;
 
 namespace HeuristicLab.Clients.Hive.Administrator.Views {
-  [View("SlaveView")]
-  [Content(typeof(Resource), IsDefaultView = true)]
-  public partial class SlaveView : ItemView {
-    public new Resource Content {
-      get { return (Resource)base.Content; }
+  [View("Slave View")]
+  [Content(typeof(Slave), IsDefaultView = true)]
+  public sealed partial class SlaveView : ResourceView {
+    public new Slave Content {
+      get { return (Slave)base.Content; }
       set { base.Content = value; }
     }
@@ -40,123 +38,37 @@
     }
 
-    #region Register Content Events
-    protected override void DeregisterContentEvents() {
-      base.DeregisterContentEvents();
-    }
-    protected override void RegisterContentEvents() {
-      base.RegisterContentEvents();
-    }
-    #endregion
-
+    #region Overrides
     protected override void OnContentChanged() {
       base.OnContentChanged();
       if (Content == null) {
-        ShowSlaveUI(true);
-        txtName.Clear();
-        txtCPU.Clear();
-        txtDetailsDescription.Clear();
-        txtMemory.Clear();
-        txtOS.Clear();
-        txtSlaveState.Clear();
-        txtLastHeartbeat.Clear();
-        txtFreeMemory.Clear();
-        txtId.Clear();
-        txtHbIntervall.Clear();
-        cbxDisposable.Checked = false;
-        cbxPublic.Checked = false;
+        cpuTextBox.Clear();
+        memoryTextBox.Clear();
+        operatingSystemTextBox.Clear();
+        stateTextBox.Clear();
+        lastHeartbeatTextBox.Clear();
+        disposableCheckBox.Checked = false;
       } else {
-        if (Content.GetType() == typeof(Slave)) {
-          ShowSlaveUI(true);
-          Slave ct = (Slave)Content;
-          bool authorized = UserInformation.Instance.UserExists && (ct.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions());
-          txtName.Text = ct.Name;
-          txtHbIntervall.Text = ct.HbInterval.ToString();
-          cbxPublic.Enabled = authorized;
-          cbxPublic.CheckedChanged -= new EventHandler(cbxPublic_CheckedChanged);
-          cbxPublic.Checked = ct.OwnerUserId == null;
-          cbxPublic.CheckedChanged += new EventHandler(cbxPublic_CheckedChanged);
-          txtCPU.Text = string.Format("{0} Cores @ {1} Mhz, Arch.: {2}", ct.Cores.ToString(), ct.CpuSpeed.ToString(), ct.CpuArchitecture.ToString());
-          txtDetailsDescription.Text = ct.Description;
-          txtMemory.Text = ct.Memory.ToString();
-          txtOS.Text = ct.OperatingSystem;
-          txtSlaveState.Text = ct.SlaveState.ToString();
-          txtLastHeartbeat.Text = ct.LastHeartbeat.ToString();
-          txtFreeMemory.Text = ct.FreeMemory.ToString();
-          txtId.Text = ct.Id.ToString();
-          cbxDisposable.Enabled = authorized;
-          cbxDisposable.Checked = ct.IsDisposable.GetValueOrDefault();
-        } else if (Content.GetType() == typeof(SlaveGroup)) {
-          SlaveGroup ct = (SlaveGroup)Content;
-          txtName.Text = ct.Name;
-          txtHbIntervall.Text = ct.HbInterval.ToString();
-          cbxPublic.Enabled = ct.Name != "UNGROUPED" && HiveRoles.CheckAdminUserPermissions();
-          cbxPublic.CheckedChanged -= new EventHandler(cbxPublic_CheckedChanged);
-          cbxPublic.Checked = ct.OwnerUserId == null;
-          cbxPublic.CheckedChanged += new EventHandler(cbxPublic_CheckedChanged);
-          ShowSlaveUI(false);
-        } else {
-          throw new Exception("Unknown Resource in SlaveView");
-        }
+        cpuTextBox.Text = string.Format("{0} Cores @ {1} MHz, Arch.: {2}", Content.Cores, Content.CpuSpeed, Content.CpuArchitecture);
+        memoryTextBox.Text = string.Format("{0} ({1} Free)", Content.Memory, Content.FreeMemory);
+        operatingSystemTextBox.Text = Content.OperatingSystem;
+        stateTextBox.Text = Content.SlaveState.ToString();
+        lastHeartbeatTextBox.Text = Content.LastHeartbeat.ToString();
+        disposableCheckBox.Checked = Content.IsDisposable.GetValueOrDefault();
       }
-    }
-
-    private void ShowSlaveUI(bool show) {
-      label1.Visible = show;
-      label2.Visible = show;
-      label4.Visible = show;
-      label10.Visible = show;
-      label11.Visible = show;
-      label12.Visible = show;
-      label13.Visible = show;
-      label14.Visible = show;
-      label15.Visible = show;
-      txtCPU.Visible = show;
-      txtDetailsDescription.Visible = show;
-      txtMemory.Visible = show;
-      txtOS.Visible = show;
-      txtSlaveState.Visible = show;
-      txtLastHeartbeat.Visible = show;
-      txtFreeMemory.Visible = show;
-      txtId.Visible = show;
-      txtName.Enabled = !show;
-      cbxDisposable.Visible = show;
     }
 
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      disposableCheckBox.Enabled = enabled;
     }
+    #endregion
 
-    private void txtName_TextChanged(object sender, EventArgs e) {
-      if (Content != null && Content is SlaveGroup) {
-        Content.Name = txtName.Text;
-      }
+    #region Event Handlers
+    private void disposableCheckBox_CheckedChanged(object sender, EventArgs e) {
+      if (Content != null)
+        Content.IsDisposable = disposableCheckBox.Checked;
     }
-
-    private void txtHbIntervall_TextChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        if (txtHbIntervall.Text.Length > 0) {
-          try {
-            int interval = int.Parse(txtHbIntervall.Text);
-            Content.HbInterval = interval;
-          }
-          catch (Exception) {
-            MessageBox.Show("Please enter a numeric value for the Heartbeat Interval.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            txtHbIntervall.Text = "10";
-          }
-        }
-      }
-    }
-
-    private void cbxDisposable_CheckedChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        ((Slave)Content).IsDisposable = cbxDisposable.Checked;
-      }
-    }
-
-    private void cbxPublic_CheckedChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        Content.OwnerUserId = cbxPublic.Checked ? null : new Guid?(UserInformation.Instance.User.Id);
-      }
-    }
+    #endregion
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/HeuristicLab.Clients.Hive.JobManager-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/HeuristicLab.Clients.Hive.JobManager-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/HeuristicLab.Clients.Hive.JobManager-3.3.csproj	(revision 16240)
@@ -105,4 +105,5 @@
     <Compile Include="ExtensionMethods\TreeNodeExtensions.cs" />
     <Compile Include="ListViewItemDateComparer.cs" />
+    <Compile Include="ListViewItemComparer.cs" />
     <Compile Include="MenuItems\CreateHiveJobMenuItem.cs" />
     <Compile Include="MenuItems\RunInHiveMenuItem.cs" />
@@ -159,4 +160,14 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Clients.Access\3.3\HeuristicLab.Clients.Access-3.3.csproj">
+      <Project>{494f87f4-0f25-4d33-a382-10cdb2174d48}</Project>
+      <Name>HeuristicLab.Clients.Access-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Clients.Common\3.3\HeuristicLab.Clients.Common-3.3.csproj">
+      <Project>{730a9104-d4d1-4360-966b-e49b7571dda3}</Project>
+      <Name>HeuristicLab.Clients.Common-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Clients.Hive.Views\3.3\HeuristicLab.Clients.Hive.Views-3.3.csproj">
       <Project>{E1D6C801-892A-406A-B606-F158E36DD3C3}</Project>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/ListViewItemComparer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/ListViewItemComparer.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/ListViewItemComparer.cs	(revision 16240)
@@ -0,0 +1,79 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections;
+using System.Windows.Forms;
+
+namespace HeuristicLab.Clients.Hive.JobManager {
+  public class ListViewItemComparer : IComparer {
+    private int[] cols;
+    public SortOrder[] Orders { get; set; }
+    private const string dateFormat = "dd.MM.yyyy HH:mm";
+
+    public ListViewItemComparer() {
+      cols = new []{ 0 };
+      Orders = new[] { SortOrder.Ascending };
+    }
+
+    public ListViewItemComparer(int[] columns, SortOrder[] orders) {
+      cols = columns;
+      Orders = orders;
+    }
+
+    public int Compare(object x, object y) {
+      int returnVal;
+      bool result;
+      ListViewItem listViewItemX, listViewItemY;
+      listViewItemX = x as ListViewItem;
+      listViewItemY = y as ListViewItem;
+
+      if (listViewItemX == null || listViewItemY == null) {
+        throw new ArgumentException(string.Format("The ListViewItemComparer expects ListViewItems but received {0} and {1}.",
+          x.GetType().ToString(), y.GetType().ToString()));
+      }
+
+      int cmpres = 0;
+      for (int i = 0; i < cols.Length && cmpres == 0; i++) {
+        string textX = listViewItemX.SubItems[cols[i]].Text;
+        string textY = listViewItemY.SubItems[cols[i]].Text;
+
+        DateTime dateX, dateY;
+        int intX, intY;
+        double doubleX, doubleY;
+
+        if(DateTime.TryParse(textX, out dateX) && DateTime.TryParse(textY, out dateY)) {
+          cmpres = DateTime.Compare(dateX, dateY);
+        } else if(Int32.TryParse(textX, out intX) && Int32.TryParse(textY, out intY)) {
+          cmpres = (intX == intY) ? 0 : (intX > intY) ? 1 : -1;
+        } else if(Double.TryParse(textX, out doubleX) && Double.TryParse(textY, out doubleY)) {
+          cmpres = (doubleX == doubleY) ? 0 : (doubleX > doubleY) ? 1 : -1;
+        } else {
+          cmpres = String.Compare(textX, textY);
+        }
+        if(Orders[i] == SortOrder.Descending) {
+          cmpres *= -1;
+        }
+      }
+      return cmpres;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/MenuItems/JobManagerMenuItem.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/MenuItems/JobManagerMenuItem.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/MenuItems/JobManagerMenuItem.cs	(revision 16240)
@@ -20,23 +20,41 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
 using HeuristicLab.MainForm;
 using HeuristicLab.Optimizer;
 
 namespace HeuristicLab.Clients.Hive.JobManager {
-  public class JobManagerMenuItem : HeuristicLab.MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
+  public class JobManagerMenuItem : MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
     public override string Name {
       get { return "&Job Manager"; }
     }
+
     public override IEnumerable<string> Structure {
       get { return new string[] { "&Services", "&Hive" }; }
     }
+
     public override void Execute() {
-      MainFormManager.MainForm.ShowContent(HiveClient.Instance);
+      if (HiveRoles.CheckHiveUserPermissions()) {
+        MainFormManager.MainForm.ShowContent(HiveClient.Instance);
+      } else if (!UserInformation.Instance.UserExists) {
+        MessageBox.Show(
+          "Couldn't fetch user information from the server." + Environment.NewLine +
+          "Please verify that you have an existing user and that your user name and password is correct.",
+          "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      } else {
+        MessageBox.Show(
+          "You do not seem to have the permissions to use the Hive Job Manager." + Environment.NewLine +
+          "If that's not the case or you have any questions please write an email to support@heuristiclab.com",
+          "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      }
     }
+
     public override int Position {
       get { return 10000; }
     }
+
     public override Keys ShortCutKeys {
       get { return Keys.Control | Keys.H; }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/MenuItems/RunInHiveMenuItem.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/MenuItems/RunInHiveMenuItem.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/MenuItems/RunInHiveMenuItem.cs	(revision 16240)
@@ -22,5 +22,8 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Hive.JobManager.Views;
 using HeuristicLab.Core;
 using HeuristicLab.MainForm;
@@ -82,9 +85,25 @@
       task.ItemTask.ComputeInParallel = content is Experiment || content is BatchRun;
 
-      progress = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().AddOperationProgressToContent(this.content, "Uploading to Hive...");
-      rJob.Progress = progress;
-      progress.ProgressStateChanged += progress_ProgressStateChanged;
+      var hiveResourceSelectorDialog = new HiveResourceSelectorDialog(rJob.Job.Id, rJob.Job.ProjectId);
 
-      HiveClient.StartJob(new Action<Exception>(HandleEx), rJob, new CancellationToken());
+      if (HiveClient.Instance.Projects.Count == 1) {
+        var project = HiveClient.Instance.Projects.FirstOrDefault();
+        if (project != null && project.Id != Guid.Empty)
+          hiveResourceSelectorDialog.SelectedProjectId = project.Id;
+      }
+
+      if (hiveResourceSelectorDialog.ShowDialog((UserControl)activeView) == DialogResult.OK) {
+        var selectedProject = hiveResourceSelectorDialog.SelectedProject;
+        if (selectedProject != null) {
+          rJob.Job.ProjectId = selectedProject.Id;
+          rJob.Job.ResourceIds = hiveResourceSelectorDialog.SelectedResources.Select(x => x.Id).ToList();
+
+          progress = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().AddOperationProgressToContent(this.content, "Uploading to Hive...");
+          rJob.Progress = progress;
+          progress.ProgressStateChanged += progress_ProgressStateChanged;
+
+          HiveClient.StartJob(new Action<Exception>(HandleEx), rJob, new CancellationToken());
+        }
+      }
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Plugin.cs.frame
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Plugin.cs.frame	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Plugin.cs.frame	(revision 16240)
@@ -25,4 +25,6 @@
   [Plugin("HeuristicLab.Clients.Hive.JobManager", "3.3.15.$WCREV$")]
   [PluginFile("HeuristicLab.Clients.Hive.JobManager-3.3.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Clients.Access", "3.3")]
+  [PluginDependency("HeuristicLab.Clients.Common", "3.3")]
   [PluginDependency("HeuristicLab.Clients.Hive", "3.3")]
   [PluginDependency("HeuristicLab.Clients.Hive.Views", "3.3")]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveJobManagerView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveJobManagerView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveJobManagerView.Designer.cs	(revision 16240)
@@ -45,6 +45,8 @@
     /// </summary>
     private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
       this.hiveExperimentListView = new RefreshableHiveJobListView();
       this.refreshButton = new System.Windows.Forms.Button();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
       this.SuspendLayout();
       // 
@@ -69,4 +71,5 @@
       this.refreshButton.Size = new System.Drawing.Size(24, 24);
       this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
       this.refreshButton.UseVisualStyleBackColor = true;
       this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
@@ -87,5 +90,5 @@
     private RefreshableHiveJobListView hiveExperimentListView;
     private System.Windows.Forms.Button refreshButton;
-
+    private System.Windows.Forms.ToolTip toolTip;
 
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelector.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelector.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelector.Designer.cs	(revision 16240)
@@ -21,5 +21,5 @@
 
 namespace HeuristicLab.Clients.Hive.JobManager.Views {
-  partial class HiveResourceSelector {
+  partial class HiveProjectSelector {
     /// <summary> 
     /// Required designer variable.
@@ -46,79 +46,57 @@
     private void InitializeComponent() {
       this.components = new System.ComponentModel.Container();
-      System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HiveResourceSelector));
-      this.resourcesGroupBox = new System.Windows.Forms.GroupBox();
-      this.splitContainer = new System.Windows.Forms.SplitContainer();
-      this.resourcesTreeView = new System.Windows.Forms.TreeView();
-      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.projectsGroupBox = new System.Windows.Forms.GroupBox();
+      this.projectsImageList = new System.Windows.Forms.ImageList(this.components);
+      this.resourcesImageList = new System.Windows.Forms.ImageList(this.components);
       this.searchLabel = new System.Windows.Forms.Label();
       this.searchTextBox = new System.Windows.Forms.TextBox();
-      this.descriptionTextBox = new System.Windows.Forms.TextBox();
       this.toolTip = new System.Windows.Forms.ToolTip(this.components);
-      this.resourcesGroupBox.SuspendLayout();
-      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
-      this.splitContainer.Panel1.SuspendLayout();
-      this.splitContainer.Panel2.SuspendLayout();
-      this.splitContainer.SuspendLayout();
+      this.splitContainer2 = new System.Windows.Forms.SplitContainer();
+      this.projectsTreeView = new System.Windows.Forms.TreeView();
+      this.resourcesTreeView = new HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView();
+      this.summaryGroupBox = new System.Windows.Forms.GroupBox();
+      this.coresLabel = new System.Windows.Forms.Label();
+      this.coresSummaryLabel = new System.Windows.Forms.Label();
+      this.memoryLabel = new System.Windows.Forms.Label();
+      this.memorySummaryLabel = new System.Windows.Forms.Label();
+      this.projectsGroupBox.SuspendLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
+      this.splitContainer2.Panel1.SuspendLayout();
+      this.splitContainer2.Panel2.SuspendLayout();
+      this.splitContainer2.SuspendLayout();
+      this.summaryGroupBox.SuspendLayout();
       this.SuspendLayout();
       // 
-      // resourcesGroupBox
-      // 
-      this.resourcesGroupBox.Controls.Add(this.splitContainer);
-      this.resourcesGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
-      this.resourcesGroupBox.Location = new System.Drawing.Point(0, 0);
-      this.resourcesGroupBox.Name = "resourcesGroupBox";
-      this.resourcesGroupBox.Size = new System.Drawing.Size(308, 365);
-      this.resourcesGroupBox.TabIndex = 0;
-      this.resourcesGroupBox.TabStop = false;
-      this.resourcesGroupBox.Text = "Available Resources";
-      // 
-      // splitContainer
-      // 
-      this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
-      this.splitContainer.Location = new System.Drawing.Point(3, 16);
-      this.splitContainer.Name = "splitContainer";
-      this.splitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
-      // 
-      // splitContainer.Panel1
-      // 
-      this.splitContainer.Panel1.Controls.Add(this.resourcesTreeView);
-      this.splitContainer.Panel1.Controls.Add(this.searchLabel);
-      this.splitContainer.Panel1.Controls.Add(this.searchTextBox);
-      // 
-      // splitContainer.Panel2
-      // 
-      this.splitContainer.Panel2.Controls.Add(this.descriptionTextBox);
-      this.splitContainer.Size = new System.Drawing.Size(302, 346);
-      this.splitContainer.SplitterDistance = 219;
-      this.splitContainer.TabIndex = 0;
-      // 
-      // resourcesTreeView
-      // 
-      this.resourcesTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.resourcesTreeView.CheckBoxes = true;
-      this.resourcesTreeView.HideSelection = false;
-      this.resourcesTreeView.ImageIndex = 0;
-      this.resourcesTreeView.ImageList = this.imageList;
-      this.resourcesTreeView.Location = new System.Drawing.Point(3, 29);
-      this.resourcesTreeView.Name = "resourcesTreeView";
-      this.resourcesTreeView.SelectedImageIndex = 0;
-      this.resourcesTreeView.ShowNodeToolTips = true;
-      this.resourcesTreeView.Size = new System.Drawing.Size(296, 196);
-      this.resourcesTreeView.TabIndex = 2;
-      this.resourcesTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.resourcesTreeView_AfterCheck);
-      this.resourcesTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.resourcesTreeView_AfterSelect);
-      // 
-      // imageList
-      // 
-      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
-      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
-      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // projectsGroupBox
+      // 
+      this.projectsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectsGroupBox.Controls.Add(this.searchLabel);
+      this.projectsGroupBox.Controls.Add(this.splitContainer2);
+      this.projectsGroupBox.Controls.Add(this.searchTextBox);
+      this.projectsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.projectsGroupBox.Name = "projectsGroupBox";
+      this.projectsGroupBox.Size = new System.Drawing.Size(426, 461);
+      this.projectsGroupBox.TabIndex = 0;
+      this.projectsGroupBox.TabStop = false;
+      this.projectsGroupBox.Text = "Available Projects";
+      // 
+      // projectsImageList
+      // 
+      this.projectsImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.projectsImageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.projectsImageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // resourcesImageList
+      // 
+      this.resourcesImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.resourcesImageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.resourcesImageList.TransparentColor = System.Drawing.Color.Transparent;
       // 
       // searchLabel
       // 
       this.searchLabel.Image = HeuristicLab.Common.Resources.VSImageLibrary.Zoom;
-      this.searchLabel.Location = new System.Drawing.Point(3, 3);
+      this.searchLabel.Location = new System.Drawing.Point(6, 19);
       this.searchLabel.Name = "searchLabel";
       this.searchLabel.Size = new System.Drawing.Size(20, 20);
@@ -128,42 +106,132 @@
       // searchTextBox
       // 
-      this.searchTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.searchTextBox.Location = new System.Drawing.Point(29, 3);
+      this.searchTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.searchTextBox.Location = new System.Drawing.Point(32, 19);
       this.searchTextBox.Name = "searchTextBox";
-      this.searchTextBox.Size = new System.Drawing.Size(270, 20);
+      this.searchTextBox.Size = new System.Drawing.Size(388, 20);
       this.searchTextBox.TabIndex = 1;
       this.toolTip.SetToolTip(this.searchTextBox, "Enter string to search for resources");
       this.searchTextBox.TextChanged += new System.EventHandler(this.searchTextBox_TextChanged);
       // 
-      // descriptionTextBox
-      // 
-      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.descriptionTextBox.Font = new System.Drawing.Font("Courier New", 8.25F);
-      this.descriptionTextBox.Location = new System.Drawing.Point(3, 3);
-      this.descriptionTextBox.Multiline = true;
-      this.descriptionTextBox.Name = "descriptionTextBox";
-      this.descriptionTextBox.ReadOnly = true;
-      this.descriptionTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
-      this.descriptionTextBox.Size = new System.Drawing.Size(296, 108);
-      this.descriptionTextBox.TabIndex = 0;
-      this.descriptionTextBox.WordWrap = false;
-      // 
-      // HiveResourceSelector
-      // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      // splitContainer2
+      // 
+      this.splitContainer2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer2.Location = new System.Drawing.Point(6, 45);
+      this.splitContainer2.Name = "splitContainer2";
+      // 
+      // splitContainer2.Panel1
+      // 
+      this.splitContainer2.Panel1.Controls.Add(this.projectsTreeView);
+      // 
+      // splitContainer2.Panel2
+      // 
+      this.splitContainer2.Panel2.Controls.Add(this.resourcesTreeView);
+      this.splitContainer2.Size = new System.Drawing.Size(414, 410);
+      this.splitContainer2.SplitterDistance = 204;
+      this.splitContainer2.TabIndex = 4;
+      // 
+      // projectsTreeView
+      // 
+      this.projectsTreeView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectsTreeView.HideSelection = false;
+      this.projectsTreeView.ImageIndex = 0;
+      this.projectsTreeView.ImageList = this.projectsImageList;
+      this.projectsTreeView.Location = new System.Drawing.Point(0, 0);
+      this.projectsTreeView.Name = "projectsTreeView";
+      this.projectsTreeView.SelectedImageIndex = 0;
+      this.projectsTreeView.ShowNodeToolTips = true;
+      this.projectsTreeView.Size = new System.Drawing.Size(204, 410);
+      this.projectsTreeView.TabIndex = 3;
+      this.projectsTreeView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.projectsTreeView_MouseDoubleClick);
+      this.projectsTreeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.projectsTreeView_BeforeSelect);
+      this.projectsTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.projectsTreeView_AfterSelect);
+      // 
+      // resourcesTreeView
+      // 
+      this.resourcesTreeView.CheckBoxes = true;
+      this.resourcesTreeView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.resourcesTreeView.ImageIndex = 0;
+      this.resourcesTreeView.ImageList = this.resourcesImageList;
+      this.resourcesTreeView.Location = new System.Drawing.Point(0, 0);
+      this.resourcesTreeView.Name = "resourcesTreeView";
+      this.resourcesTreeView.SelectedImageIndex = 0;
+      this.resourcesTreeView.Size = new System.Drawing.Size(206, 410);
+      this.resourcesTreeView.TabIndex = 0;
+      this.resourcesTreeView.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.resourcesTreeView_NodeMouseDoubleClick);
+      this.resourcesTreeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.resourcesTreeView_BeforeCheck);
+      this.resourcesTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.resourcesTreeView_AfterCheck);
+      this.resourcesTreeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.resourcesTreeView_MouseDown);
+      this.resourcesTreeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.resourcesTreeView_BeforeSelect);
+      // 
+      // summaryGroupBox
+      // 
+      this.summaryGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.summaryGroupBox.Controls.Add(this.memorySummaryLabel);
+      this.summaryGroupBox.Controls.Add(this.memoryLabel);
+      this.summaryGroupBox.Controls.Add(this.coresSummaryLabel);
+      this.summaryGroupBox.Controls.Add(this.coresLabel);
+      this.summaryGroupBox.Location = new System.Drawing.Point(3, 470);
+      this.summaryGroupBox.Name = "summaryGroupBox";
+      this.summaryGroupBox.Size = new System.Drawing.Size(426, 71);
+      this.summaryGroupBox.TabIndex = 1;
+      this.summaryGroupBox.TabStop = false;
+      this.summaryGroupBox.Text = "Computing Resources";
+      // 
+      // coresLabel
+      // 
+      this.coresLabel.AutoSize = true;
+      this.coresLabel.Location = new System.Drawing.Point(6, 22);
+      this.coresLabel.Name = "coresLabel";
+      this.coresLabel.Size = new System.Drawing.Size(37, 13);
+      this.coresLabel.TabIndex = 1;
+      this.coresLabel.Text = "Cores:";
+      // 
+      // coresSummaryLabel
+      // 
+      this.coresSummaryLabel.AutoSize = true;
+      this.coresSummaryLabel.Location = new System.Drawing.Point(59, 22);
+      this.coresSummaryLabel.Name = "coresSummaryLabel";
+      this.coresSummaryLabel.Size = new System.Drawing.Size(124, 13);
+      this.coresSummaryLabel.TabIndex = 5;
+      this.coresSummaryLabel.Text = "0 Total (0 Free / 0 Used)";
+      // 
+      // memoryLabel
+      // 
+      this.memoryLabel.AutoSize = true;
+      this.memoryLabel.Location = new System.Drawing.Point(6, 48);
+      this.memoryLabel.Name = "memoryLabel";
+      this.memoryLabel.Size = new System.Drawing.Size(47, 13);
+      this.memoryLabel.TabIndex = 7;
+      this.memoryLabel.Text = "Memory:";
+      // 
+      // memorySummaryLabel
+      // 
+      this.memorySummaryLabel.AutoSize = true;
+      this.memorySummaryLabel.Location = new System.Drawing.Point(59, 48);
+      this.memorySummaryLabel.Name = "memorySummaryLabel";
+      this.memorySummaryLabel.Size = new System.Drawing.Size(223, 13);
+      this.memorySummaryLabel.TabIndex = 8;
+      this.memorySummaryLabel.Text = "0.00 GB Total (0.00 GB Free / 0.00 GB Used)";
+      // 
+      // HiveProjectSelector
+      // 
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
-      this.Controls.Add(this.resourcesGroupBox);
-      this.Name = "HiveResourceSelector";
-      this.Size = new System.Drawing.Size(308, 365);
-      this.resourcesGroupBox.ResumeLayout(false);
-      this.splitContainer.Panel1.ResumeLayout(false);
-      this.splitContainer.Panel1.PerformLayout();
-      this.splitContainer.Panel2.ResumeLayout(false);
-      this.splitContainer.Panel2.PerformLayout();
-      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
-      this.splitContainer.ResumeLayout(false);
+      this.Controls.Add(this.summaryGroupBox);
+      this.Controls.Add(this.projectsGroupBox);
+      this.Name = "HiveProjectSelector";
+      this.Size = new System.Drawing.Size(432, 544);
+      this.Load += new System.EventHandler(this.HiveProjectSelector_Load);
+      this.projectsGroupBox.ResumeLayout(false);
+      this.projectsGroupBox.PerformLayout();
+      this.splitContainer2.Panel1.ResumeLayout(false);
+      this.splitContainer2.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
+      this.splitContainer2.ResumeLayout(false);
+      this.summaryGroupBox.ResumeLayout(false);
+      this.summaryGroupBox.PerformLayout();
       this.ResumeLayout(false);
 
@@ -172,13 +240,18 @@
     #endregion
 
-    protected System.Windows.Forms.GroupBox resourcesGroupBox;
-    protected System.Windows.Forms.SplitContainer splitContainer;
+    protected System.Windows.Forms.GroupBox projectsGroupBox;
     protected System.Windows.Forms.Label searchLabel;
     protected System.Windows.Forms.TextBox searchTextBox;
-    protected System.Windows.Forms.TreeView resourcesTreeView;
-    protected System.Windows.Forms.TextBox descriptionTextBox;
-    protected System.Windows.Forms.ImageList imageList;
+    protected System.Windows.Forms.ImageList projectsImageList;
     protected System.Windows.Forms.ToolTip toolTip;
-
+    private System.Windows.Forms.ImageList resourcesImageList;
+    private System.Windows.Forms.SplitContainer splitContainer2;
+    protected System.Windows.Forms.TreeView projectsTreeView;
+    protected HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView resourcesTreeView;
+    private System.Windows.Forms.GroupBox summaryGroupBox;
+    protected System.Windows.Forms.Label coresLabel;
+    protected System.Windows.Forms.Label memorySummaryLabel;
+    protected System.Windows.Forms.Label memoryLabel;
+    protected System.Windows.Forms.Label coresSummaryLabel;
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelector.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelector.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelector.cs	(revision 16240)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -24,7 +24,8 @@
 using System.Drawing;
 using System.Linq;
-using System.Text;
 using System.Windows.Forms;
-using HeuristicLab.Clients.Hive.JobManager.ExtensionMethods;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Common.Resources;
 using HeuristicLab.Core;
 using HeuristicLab.Core.Views;
@@ -33,295 +34,818 @@
 
 namespace HeuristicLab.Clients.Hive.JobManager.Views {
-  [View("Hive Resource Selector View")]
-  [Content(typeof(IItemList<Resource>), true)]
-  public partial class HiveResourceSelector : ItemView, IDisposable {
+  [View("Hive Project Selector View")]
+  [Content(typeof(IItemList<Project>), true)]
+  public partial class HiveProjectSelector : ItemView, IDisposable {
+    private const int greenFlagImageIndex = 0;
+    private const int redFlagImageIndex = 1;
     private const int slaveImageIndex = 0;
     private const int slaveGroupImageIndex = 1;
+    public const string additionalSlavesGroupName = "Additional Slaves";
+    public const string additionalSlavesGroupDescription = "Contains additional slaves which are either ungrouped or the parenting slave group is not assigned to the selected project.";
+
+    private const string CURRENT_SELECTION_TAG = " [current assignment]";
+    private const string NEW_SELECTION_TAG = " [new assignment]";
+    private const string CHANGED_SELECTION_TAG = " [changed assignment]";
+    private const string ADDED_SELECTION_TAG = " [added assignment]";
+    private const string REMOVED_SELECTION_TAG = " [removed assignment]";
+    private const string SELECTED_TAG = " [assigned]";
+    private const string INCLUDED_TAG = " [included]";
+    private const string ADDED_INCLUDE_TAG = " [added include]";
+    private const string REMOVED_INCLUDE_TAG = " [removed include]";
+
+    private TreeNode additionalNode;
+
+    private readonly HashSet<TreeNode> mainTreeNodes = new HashSet<TreeNode>();
+    private readonly HashSet<TreeNode> filteredTreeNodes = new HashSet<TreeNode>();
+
+    private readonly HashSet<Resource> availableResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> includedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> newAssignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> newIncludedResources = new HashSet<Resource>();
+
+    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
+    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
+    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color controlTextColor = SystemColors.ControlText;
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
     private string currentSearchString;
-    private ISet<TreeNode> mainTreeNodes;
-    private ISet<TreeNode> filteredTreeNodes;
-    private ISet<TreeNode> nodeStore;
-    
-    private ISet<Resource> selectedResources;
-    public ISet<Resource> SelectedResources {
-      get { return selectedResources; }
-      set { selectedResources = value; }
-    }
-
-    public new IItemList<Resource> Content {
-      get { return (IItemList<Resource>)base.Content; }
+
+    private void resetHiveResourceSelector() {
+      lastSelectedProject = null;
+      selectedProject = null;
+      projectId = null;
+    }
+
+    private Guid jobId;
+    public Guid JobId {
+      get { return jobId; }
+      set {
+        if (jobId == value) return;
+        jobId = value;
+        resetHiveResourceSelector();
+      }
+    }
+
+    private Guid? projectId;
+    public Guid? ProjectId {
+      get { return projectId; }
+      set {
+        if (projectId == value) return;
+        projectId = value;
+      }
+    }
+
+    private Guid? selectedProjectId;
+    public Guid? SelectedProjectId {
+      get { return selectedProjectId; }
+      set {
+        if (selectedProjectId == value) return;
+        selectedProjectId = value;
+      }
+    }
+
+    private IEnumerable<Guid> selectedResourceIds;
+    public IEnumerable<Guid> SelectedResourceIds {
+      get { return selectedResourceIds; }
+      set {
+        if (selectedResourceIds == value) return;
+        selectedResourceIds = value;
+      }
+    }
+
+    public bool ChangedProjectSelection {
+      get {
+        if ((lastSelectedProject == null && selectedProject != null) 
+          || (lastSelectedProject != null && selectedProject == null)
+          || (lastSelectedProject != null && selectedProject != null && lastSelectedProject.Id != selectedProject.Id))
+          return true;
+        else return false;
+      }
+    }
+
+    public bool ChangedResources {
+      get { return !assignedResources.SetEquals(newAssignedResources); }
+    }
+
+    private Project lastSelectedProject;
+    private Project selectedProject;
+    public Project SelectedProject {
+      get { return selectedProject; }
+      set {
+        if (selectedProject == value) return;
+
+        if ((JobId == Guid.Empty || JobId == null) 
+          && (value == null || SelectedProject == null || value.Id != SelectedProject.Id)) selectedResourceIds = null;
+        lastSelectedProject = selectedProject;
+        selectedProject = value;
+
+        UpdateResourceTree();
+        ExtractStatistics();
+        OnSelectedProjectChanged();
+      }
+    }
+
+    public int AssignedCores {
+      get {
+        HashSet<Slave> newAssignedSlaves = new HashSet<Slave>(newAssignedResources.OfType<Slave>());
+        foreach(var slaveGroup in newAssignedResources.OfType<SlaveGroup>()) {
+          foreach(var slave in HiveClient.Instance.GetAvailableResourceDescendants(slaveGroup.Id).OfType<Slave>()) {
+            newAssignedSlaves.Add(slave);
+          }
+        }
+        return newAssignedSlaves.Sum(x => x.Cores.GetValueOrDefault());
+      }
+    }
+
+    public IEnumerable<Resource> AssignedResources {
+      get { return newAssignedResources; }
+      set {
+        if (newAssignedResources == value) return;
+        newAssignedResources.Clear();
+        foreach(var resource in value) {
+          newAssignedResources.Add(resource);
+        }
+      }
+    }
+
+
+    public new IItemList<Project> Content {
+      get { return (IItemList<Project>)base.Content; }
       set { base.Content = value; }
     }
 
-    public HiveResourceSelector() {
+    public HiveProjectSelector() {
       InitializeComponent();
-      mainTreeNodes = new HashSet<TreeNode>();
-      filteredTreeNodes = new HashSet<TreeNode>();
-      nodeStore = new HashSet<TreeNode>();
-      selectedResources = new HashSet<Resource>();
-      imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.MonitorLarge);
-      imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NetworkCenterLarge);
-    }
-  
-    public void StartProgressView() {
-      if (InvokeRequired) {
-        Invoke(new Action(StartProgressView));
-      } else {
-        var message = "Downloading resources. Please be patient.";
-        MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, message);
-      }
-    }
-
-    public void FinishProgressView() {
-      if (InvokeRequired) {
-        Invoke(new Action(FinishProgressView));
-      } else {
-        MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this);
-      }
-    }
-
+
+      projectsImageList.Images.Add(VSImageLibrary.FlagGreen);
+      projectsImageList.Images.Add(VSImageLibrary.FlagRed);
+      resourcesImageList.Images.Add(VSImageLibrary.MonitorLarge);
+      resourcesImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
+    }
+
+    #region Overrides
     protected override void OnContentChanged() {
       base.OnContentChanged();
 
-      if (Content != null) {
-        selectedResources = new HashSet<Resource>(Content.Where(x => selectedResources.Any(y => x.Id == y.Id)));
-        UpdateMainTree();
+      if (Content != null) {               
+        if (SelectedProjectId.HasValue && SelectedProjectId.Value != Guid.Empty) {
+          SelectedProject = GetSelectedProjectById(SelectedProjectId.Value);
+        } else {
+          SelectedProject = null;
+        }
+        //ExtractStatistics();
+        UpdateProjectTree();
+
+      } else {
+        lastSelectedProject = null;
+        selectedProject = null;
+        selectedProjectId = null;
+        projectsTreeView.Nodes.Clear();
+        resourcesTreeView.Nodes.Clear();
+      }
+    }
+
+    #endregion
+
+    #region Event Handlers
+    private void HiveProjectSelector_Load(object sender, EventArgs e) {
+      projectsTreeView.Nodes.Clear();
+      resourcesTreeView.Nodes.Clear();
+    }
+
+    private void searchTextBox_TextChanged(object sender, EventArgs e) {
+      currentSearchString = searchTextBox.Text.ToLower();
+      //UpdateFilteredTree();
+      UpdateProjectTree();
+    }
+
+    private void projectsTreeView_MouseDoubleClick(object sender, MouseEventArgs e) {
+      OnProjectsTreeViewDoubleClicked();
+    }
+
+    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      var node = (Project)e.Node.Tag;
+      if (HiveClient.Instance.DisabledParentProjects.Contains(node)) {
+        e.Cancel = true;
+      }
+    }
+
+    private void projectsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var node = (Project)e.Node.Tag;
+
+      if (node == null) {
+        projectsTreeView.SelectedNode = null;
+      } else if (HiveClient.Instance.DisabledParentProjects.Contains(node)) {
+        return;
+      } else {
+        ResetTreeNodes(projectsTreeView.Nodes);
+        e.Node.BackColor = selectedBackColor;
+        e.Node.ForeColor = selectedForeColor;
+
+        if (node.Id == projectId) {
+          e.Node.Text += CURRENT_SELECTION_TAG;
+        } else if (projectId == null || projectId == Guid.Empty) {
+          e.Node.Text += NEW_SELECTION_TAG;
+        } else {
+          e.Node.Text += CHANGED_SELECTION_TAG;
+        }
+
+
+      }
+      SelectedProject = node;
+    }
+
+    private void resourcesTreeView_MouseDown(object sender, MouseEventArgs e) {
+      var node = resourcesTreeView.GetNodeAt(new Point(e.X, e.Y));
+
+      if (node == null && e.Button == MouseButtons.Left) {
+        resourcesTreeView.SelectedNode = null;
         ExtractStatistics();
+      }
+    }
+
+    private void resourcesTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      if(e.Node == null) {
+        e.Cancel = true;
+        resourcesTreeView.SelectedNode = null;
+        ExtractStatistics();
+      } else if (e.Node == additionalNode) {
+        e.Cancel = true;
       } else {
-        mainTreeNodes.Clear();
-        UpdateFilteredTree();
-      }
-    }
-
-    #region MainTree Methods
-    private void UpdateMainTree() {
-      mainTreeNodes.Clear();
-
-      foreach (Resource g in Content) {
-        if (g.GetType() == typeof(SlaveGroup)) {
-          //root node
-          if (g.ParentResourceId == null) {
-            TreeNode tn = new TreeNode();
-            tn.ImageIndex = slaveGroupImageIndex;
-            tn.SelectedImageIndex = tn.ImageIndex;
-
-            tn.Tag = g;
-            tn.Text = g.Name;
-            tn.Checked = selectedResources.Any(x => x.Id == g.Id);
-
-            BuildMainTree(tn);
-            mainTreeNodes.Add(tn);
-          }
-        }
-      }
-      UpdateFilteredTree();
-    }
-
-    private void BuildMainTree(TreeNode tn) {
-      foreach (Resource r in Content.Where(s => s.ParentResourceId != null && s.ParentResourceId == ((Resource)tn.Tag).Id)) {
-        TreeNode stn = new TreeNode(r.Name);
-        if (r is Slave) stn.ImageIndex = slaveImageIndex;
-        else if (r is SlaveGroup) stn.ImageIndex = slaveGroupImageIndex;
-        stn.SelectedImageIndex = stn.ImageIndex;
-        stn.Tag = r;
-        stn.Checked = selectedResources.Any(x => x.Id == r.Id);
-        tn.Nodes.Add(stn);
-        mainTreeNodes.Add(stn);
-
-        BuildMainTree(stn);
-      }
+        var r = (Resource)e.Node.Tag;
+        if(r != null && !HiveClient.Instance.DisabledParentResources.Contains(r)) {
+          ExtractStatistics(r);
+        } else {
+          e.Cancel = true;
+        }
+      }
+    }
+
+    private void resourcesTreeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      if(e.Node == null || e.Node == additionalNode) {
+        e.Cancel = true;
+      } else {
+        var checkedResource = (Resource)e.Node.Tag;
+        if (checkedResource == null
+          || checkedResource.Id == Guid.Empty
+          || HiveClient.Instance.DisabledParentResources.Contains(checkedResource)
+          || newIncludedResources.Contains(checkedResource)) {
+          e.Cancel = true;
+        }
+      }
+    }
+
+    private void resourcesTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedResource = (Resource)e.Node.Tag;
+      if (e.Node.Checked) {
+        newAssignedResources.Add(checkedResource);
+      } else {
+        newAssignedResources.Remove(checkedResource);
+      }
+
+      UpdateResourceTreeAfterCheck();
+      if(resourcesTreeView.SelectedNode == null)
+        ExtractStatistics();
+      OnAssignedResourcesChanged();
+    }
+
+    private void resourcesTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) {
+      return;
     }
     #endregion
 
-    #region FilteredTree Methods
-    private void UpdateFilteredTree() {
-      filteredTreeNodes.Clear();
-      foreach (TreeNode n in mainTreeNodes) {
-        n.BackColor = SystemColors.Window;
-        if (currentSearchString == null || ((Resource)n.Tag).Name.ToLower().Contains(currentSearchString)) {
-          n.BackColor = string.IsNullOrEmpty(currentSearchString) ? SystemColors.Window : Color.LightBlue;
-          filteredTreeNodes.Add(n);
-          TraverseParentNodes(n);
-        }
-      }
-      UpdateResourceTree();
-    }
-
-    private void TraverseParentNodes(TreeNode node) {
-      if (node != null) {
-        for (TreeNode parent = node.Parent; parent != null; parent = parent.Parent)
-          filteredTreeNodes.Add(parent);
-      }
-    }
+    #region Helpers
+
+    private Project GetSelectedProjectById(Guid projectId) {
+      return Content.Where(x => x.Id == projectId).SingleOrDefault();
+    }
+
+    private void UpdateProjectTree() {
+
+      if (string.IsNullOrEmpty(currentSearchString)) {
+        BuildProjectTree(Content);
+      } else {
+        HashSet<Project> filteredProjects = new HashSet<Project>();
+        foreach(var project in Content) {
+          if(project.Name.ToLower().Contains(currentSearchString.ToLower())) {
+            filteredProjects.Add(project);
+            filteredProjects.UnionWith(Content.Where(p => HiveClient.Instance.GetAvailableProjectAncestors(project.Id).Select(x => x.Id).Contains(p.Id)));
+          }
+        }
+        BuildProjectTree(filteredProjects);
+      }
+    }
+
+    private void BuildProjectTree(IEnumerable<Project> projects) {
+      projectsTreeView.Nodes.Clear();
+      if (!projects.Any()) return;
+
+      var disabledParentProjects = HiveClient.Instance.DisabledParentProjects;
+      // select all top level projects (withouth parent, or without any ancestor within current project collection)
+      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
+      //var parentedMainProjects = new HashSet<Project>(projects
+      //  .Where(x => x.ParentProjectId.HasValue
+      //  && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)
+      //  && !projects.SelectMany(y => HiveClient.Instance.ProjectAncestors[y.Id]).Contains(x.ParentProjectId.Value)));
+      //mainProjects.UnionWith(parentedMainProjects);
+      var mainDisabledParentProjects = new HashSet<Project>(disabledParentProjects.Where(x => x.ParentProjectId == null || x.ParentProjectId == Guid.Empty));
+      mainProjects.UnionWith(mainDisabledParentProjects);
+      var subProbjects = new HashSet<Project>(projects.Union(disabledParentProjects).Except(mainProjects));
+      //foreach (var p in subProbjects) {
+      //  p.ParentProjectId = HiveClient.Instance.ProjectAncestors[p.Id].Where(x => projects.Select(y => y.Id).Contains(x)).FirstOrDefault();
+      //}
+
+      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
+
+      TreeNode currentNode = null;
+      Project currentProject = null;
+
+      while(stack.Any()) {
+        var newProject = stack.Pop();
+        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
+
+        while (currentNode != null && newProject.ParentProjectId != currentProject.Id) {
+          currentNode = currentNode.Parent;
+          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          projectsTreeView.Nodes.Add(newNode);
+          newNode.ImageIndex = greenFlagImageIndex;
+        } else {
+          currentNode.Nodes.Add(newNode);
+          newNode.ImageIndex = redFlagImageIndex;
+        }
+
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+
+        if(disabledParentProjects.Contains(newProject)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        }
+        else if (SelectedProject != null && SelectedProject.Id.Equals(newProject.Id)) {
+          newNode.BackColor = selectedBackColor;
+          newNode.ForeColor = selectedForeColor;
+          if(SelectedProject.Id == projectId) {
+            newNode.Text += CURRENT_SELECTION_TAG;
+          } else if (projectId == null || projectId == Guid.Empty) {
+            newNode.Text += NEW_SELECTION_TAG;
+          } else {
+            newNode.Text += CHANGED_SELECTION_TAG;
+          }
+        }
+
+        if (!string.IsNullOrEmpty(currentSearchString) && newProject.Name.ToLower().Contains(currentSearchString.ToLower())) {
+          newNode.BackColor = Color.LightBlue;
+        }
+
+        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
+        if (childProjects.Any()) {
+          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
+            subProbjects.Remove(project);
+            stack.Push(project);
+          }
+          currentNode = newNode;
+          currentProject = newProject;
+        }
+      }
+
+      projectsTreeView.ExpandAll();
+    }
+
+    private static IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
+      var assignedProjectResources = HiveServiceLocator.Instance.CallHiveService(s => s.GetAssignedResourcesForProject(projectId));
+      return HiveClient.Instance.Resources.Where(x => assignedProjectResources.Select(y => y.ResourceId).Contains(x.Id));
+    }
+
+    private static IEnumerable<Resource> GetAssignedResourcesForJob(Guid jobId) {
+      var assignedJobResources = HiveServiceLocator.Instance.CallHiveService(s => s.GetAssignedResourcesForJob(jobId));
+      return HiveClient.Instance.Resources.Where(x => assignedJobResources.Select(y => y.ResourceId).Contains(x.Id));
+    }
+
+    private void UpdateResourceTree() {
+      UpdateAvailableResources();
+      UpdateAssignedResources();
+      UpdateIncludedResources();
+      BuildResourceTree(availableResources);
+    }
+
+    private void UpdateResourceTreeAfterCheck() {
+      resourcesTreeView.BeforeCheck -= resourcesTreeView_BeforeCheck;
+      resourcesTreeView.AfterCheck -= resourcesTreeView_AfterCheck;
+      UpdateNewAssignedResources();
+      UpdateNewIncludedResources();
+      SetTreeNodes(resourcesTreeView.Nodes);
+      resourcesTreeView.BeforeCheck += resourcesTreeView_BeforeCheck;
+      resourcesTreeView.AfterCheck += resourcesTreeView_AfterCheck;
+    }
+
+    private void UpdateAvailableResources() {
+      availableResources.Clear();
+      if (selectedProject != null) {
+        var assignedProjectResources = GetAssignedResourcesForProject(selectedProject.Id);
+        foreach (var resource in assignedProjectResources) {
+          availableResources.Add(resource);
+          foreach(var descendant in HiveClient.Instance.Resources.Where(x => HiveClient.Instance.ResourceDescendants[resource.Id].Contains(x.Id))) {
+            availableResources.Add(descendant);
+          }
+        }
+      }
+      //ExtractStatistics();
+      //OnAssignedResourcesChanged();
+    }
+
+    private void UpdateAssignedResources() {
+      assignedResources.Clear();
+      newAssignedResources.Clear();
+
+      if (JobId == Guid.Empty || JobId == null) { // new, unchanged jobs get all avaialable resources
+        // update new assigned resources
+        if(selectedResourceIds == null) {
+          foreach (var resource in availableResources
+            .Where(x => !x.ParentResourceId.HasValue
+            || !availableResources.Select(y => y.Id).Contains(x.ParentResourceId.Value))) {
+            newAssignedResources.Add(resource);
+          }
+        } else {
+          foreach(var resource in availableResources.Where(x => selectedResourceIds.Contains(x.Id))) {
+            newAssignedResources.Add(resource);
+          }
+        }
+      } else if(selectedProject.Id == projectId) { // existent, unchanged jobs get all assigned resources
+        // update assigned resources
+        var assignedJobResources = GetAssignedResourcesForJob(JobId);
+        foreach (var resource in assignedJobResources) {
+          assignedResources.Add(resource);
+          if (selectedResourceIds == null) {
+            newAssignedResources.Add(resource);
+          }
+        }
+
+        if(selectedResourceIds != null) {
+          foreach (var resource in availableResources.Where(x => selectedResourceIds.Contains(x.Id))) {
+            newAssignedResources.Add(resource);
+          }
+        }
+      }
+
+      //ExtractStatistics();
+      OnAssignedResourcesChanged();
+    }
+
+    private void UpdateNewAssignedResources() {
+      for(int i = newAssignedResources.Count-1; i>=0; i--) {
+        if(newAssignedResources.Intersect(HiveClient.Instance.GetAvailableResourceAncestors(newAssignedResources.ElementAt(i).Id)).Any()) {
+          newAssignedResources.Remove(newAssignedResources.ElementAt(i));
+        }
+      }
+    }
+
+    private void UpdateIncludedResources() {
+      includedResources.Clear();
+      newIncludedResources.Clear();
+
+      if (JobId != Guid.Empty) {
+        foreach (var item in assignedResources) {
+          foreach (var descendant in HiveClient.Instance.GetAvailableResourceDescendants(item.Id)) {
+            includedResources.Add(descendant);
+          }
+        }
+      }
+
+      foreach (var item in newAssignedResources) {
+        foreach (var descendant in HiveClient.Instance.GetAvailableResourceDescendants(item.Id)) {
+          newIncludedResources.Add(descendant);
+        }
+      }
+    }
+
+    private void UpdateNewIncludedResources() {
+      newIncludedResources.Clear();
+      foreach (var item in newAssignedResources) {
+        foreach (var descendant in HiveClient.Instance.GetAvailableResourceDescendants(item.Id)) {
+          newIncludedResources.Add(descendant);
+        }
+      }
+    }
+
+    private void BuildResourceTree(IEnumerable<Resource> resources) {
+      resourcesTreeView.Nodes.Clear();
+      if (!resources.Any()) return;
+
+      resourcesTreeView.BeforeCheck -= resourcesTreeView_BeforeCheck;
+      resourcesTreeView.AfterCheck -= resourcesTreeView_AfterCheck;
+
+      //var disabledParentResources = HiveClient.Instance.DisabledParentResources;
+      var disabledParentResources = HiveClient.Instance.GetDisabledResourceAncestors(resources);
+      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>().Where(x => x.ParentResourceId == null));
+      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
+      //mainResources.UnionWith(parentedMainResources);
+      //var subResources = new HashSet<Resource>(resources.Except(mainResources));
+      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
+      mainResources.UnionWith(mainDisabledParentResources);
+      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
+
+      var addedAssignments = newAssignedResources.Except(assignedResources);
+      var removedAssignments = assignedResources.Except(newAssignedResources);
+      var addedIncludes = newIncludedResources.Except(includedResources);
+      var removedIncludes = includedResources.Except(newIncludedResources);
+
+      TreeNode currentNode = null;
+      Resource currentResource = null;
+
+      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
+
+      while (stack.Any()) {
+        var newResource = stack.Pop();
+        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
+
+        // search for parent node of newNode and save in currentNode
+        // necessary since newNodes (stack top items) might be siblings
+        // or grand..grandparents of previous node (currentNode)
+        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
+          currentNode = currentNode.Parent;
+          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          resourcesTreeView.Nodes.Add(newNode);
+        } else {
+          currentNode.Nodes.Add(newNode);
+        }
+
+        if (disabledParentResources.Contains(newResource)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        } else if (newAssignedResources.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.Checked = true;
+          if(!addedAssignments.Select(x => x.Id).Contains(newResource.Id) && !removedAssignments.Select(x => x.Id).Contains(newResource.Id)) {
+            newNode.Text += SELECTED_TAG;
+          }
+        } else if (newIncludedResources.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.Checked = true;
+          newNode.ForeColor = grayTextColor;
+        }
+
+        if (includedResources.Select(x => x.Id).Contains(newResource.Id) && newIncludedResources.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.Text += INCLUDED_TAG;
+        } else if (addedIncludes.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.BackColor = addedIncludeColor;
+          newNode.ForeColor = grayTextColor;
+          newNode.Text += ADDED_INCLUDE_TAG;
+        } else if (removedIncludes.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.BackColor = removedIncludeColor;
+          newNode.Text += REMOVED_INCLUDE_TAG;
+        }
+
+        if (addedAssignments.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.BackColor = addedAssignmentColor;
+          newNode.ForeColor = controlTextColor;
+          newNode.Text += ADDED_SELECTION_TAG;
+        } else if (removedAssignments.Select(x => x.Id).Contains(newResource.Id)) {
+          newNode.BackColor = removedAssignmentColor;
+          newNode.ForeColor = controlTextColor;
+          newNode.Text += REMOVED_SELECTION_TAG;
+        }
+
+        if (newResource is Slave) {
+          newNode.ImageIndex = slaveImageIndex;
+        } else {
+          newNode.ImageIndex = slaveGroupImageIndex;
+
+          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
+          if (childResources.Any()) {
+            foreach (var resource in childResources.OrderByDescending(x => x.Name)) {
+              subResources.Remove(resource);
+              stack.Push(resource);
+            }
+            currentNode = newNode;
+            currentResource = newResource;
+          }
+        }
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+      }
+
+      var singleSlaves = subResources.OfType<Slave>();
+      if (singleSlaves.Any()) {
+
+        additionalNode = new TreeNode(additionalSlavesGroupName) {
+          ForeColor = SystemColors.GrayText,
+          ImageIndex = slaveGroupImageIndex,
+          Tag = new SlaveGroup() {
+            Name = additionalSlavesGroupName,
+            Description = additionalSlavesGroupDescription
+          }
+        };
+
+        foreach (var slave in singleSlaves.OrderBy(x => x.Name)) {
+          var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+
+          if (newAssignedResources.Select(x => x.Id).Contains(slave.Id)) {
+            slaveNode.Checked = true;
+            if (!addedAssignments.Select(x => x.Id).Contains(slave.Id) &&
+                !removedAssignments.Select(x => x.Id).Contains(slave.Id)) {
+              slaveNode.Text += SELECTED_TAG;
+            }
+          }
+
+          if (addedAssignments.Select(x => x.Id).Contains(slave.Id)) {
+            slaveNode.BackColor = addedAssignmentColor;
+            slaveNode.ForeColor = controlTextColor;
+            slaveNode.Text += ADDED_SELECTION_TAG;
+          } else if (removedAssignments.Select(x => x.Id).Contains(slave.Id)) {
+            slaveNode.BackColor = removedAssignmentColor;
+            slaveNode.ForeColor = controlTextColor;
+            slaveNode.Text += REMOVED_SELECTION_TAG;
+          }
+
+          additionalNode.Nodes.Add(slaveNode);
+        }
+
+        resourcesTreeView.Nodes.Add(additionalNode);
+      }
+
+      ExpandResourceNodesOfInterest(resourcesTreeView.Nodes);
+
+      resourcesTreeView.BeforeCheck += resourcesTreeView_BeforeCheck;
+      resourcesTreeView.AfterCheck += resourcesTreeView_AfterCheck;
+    }
+
+    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
+      foreach(TreeNode n in nodes) {
+        Resource r = (Resource)n.Tag;
+        if(n.Nodes.Count > 0) {
+          if(HiveClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()            
+            || HiveClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<Slave>().Intersect(assignedResources.Union(newAssignedResources)).Any()
+            || (n == additionalNode && additionalNode.Nodes.Count > 0 && additionalNode.Nodes.Cast<TreeNode>().Any(x => x.Checked))) {
+            n.Expand();
+            ExpandResourceNodesOfInterest(n.Nodes);
+          } else {
+            n.Collapse();
+          }   
+        } else {
+          n.Collapse();
+        }
+      }
+    }
+
+    private void CollapseSlaveOnlyNodes(TreeNode tn) {
+      if (!(tn.Tag is Resource)) return;
+
+      Resource r = (Resource)tn.Tag;
+      if (HiveClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
+        tn.Expand();
+        foreach (TreeNode n in tn.Nodes) CollapseSlaveOnlyNodes(n);
+      } else {
+        tn.Collapse();
+      }
+    }
+
+    private void ExtractStatistics(Resource resource = null) { 
+      HashSet<Slave> newAssignedSlaves = new HashSet<Slave>(newAssignedResources.OfType<Slave>());
+      foreach (var slaveGroup in newAssignedResources.OfType<SlaveGroup>()) {
+        foreach (var slave in HiveClient.Instance.GetAvailableResourceDescendants(slaveGroup.Id).OfType<Slave>()) {
+          newAssignedSlaves.Add(slave);
+        }
+      }
+
+      HashSet<Slave> selectedSlaves = null;
+
+      if (resource != null) {
+        var slaveGroup = resource as SlaveGroup;
+        if (slaveGroup != null) {
+          selectedSlaves = new HashSet<Slave>(HiveClient.Instance.GetAvailableResourceDescendants(slaveGroup.Id).OfType<Slave>());
+        } else {
+          selectedSlaves = new HashSet<Slave>(new[] { resource as Slave });
+        }
+      } else {
+        selectedSlaves = newAssignedSlaves;
+      }
+
+      int sumCores = selectedSlaves.Sum(x => x.Cores.GetValueOrDefault());
+      int sumFreeCores = selectedSlaves.Sum(x => x.FreeCores.GetValueOrDefault());
+      double sumMemory = selectedSlaves.Sum(x => x.Memory.GetValueOrDefault()) / 1024.0;
+      double sumFreeMemory = selectedSlaves.Sum(x => x.FreeMemory.GetValueOrDefault()) / 1024.0;
+
+      coresSummaryLabel.Text = string.Format("{0} Total ({1} Free / {2} Used)", sumCores, sumFreeCores, sumCores - sumFreeCores);
+      memorySummaryLabel.Text = string.Format("{0:0.00} GB Total ({1:0.00} GB Free / {2:0.00} GB Used)", sumMemory, sumFreeMemory, sumMemory - sumFreeMemory);
+    }
+
+    private void StyleTreeNode(TreeNode n, string name) {
+      n.Text = name;
+      n.BackColor = Color.Transparent;
+      n.ForeColor = Color.Black;
+
+      if(n.Tag is Project) {
+        var p = (Project)n.Tag;
+        if(HiveClient.Instance.DisabledParentProjects.Select(x => x.Id).Contains(p.Id)) {
+          n.Checked = false;
+          n.ForeColor = grayTextColor;
+        }
+      } else if(n.Tag is Resource) {
+        var r = (Resource)n.Tag;
+        if(HiveClient.Instance.DisabledParentResources.Select(x => x.Id).Contains(r.Id) || n == additionalNode) {
+          n.Checked = false;
+          n.ForeColor = grayTextColor;
+        }
+      }
+    }
+
+    private void ResetTreeNodes(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        string name = "";
+        if (n.Tag is Project) name = ((Project)n.Tag).Name;
+        else if (n.Tag is Resource) name = ((Resource)n.Tag).Name;
+        StyleTreeNode(n, name);
+        if (n.Nodes.Count > 0) {
+          ResetTreeNodes(n.Nodes);
+        }
+      }
+    }
+
+    private void SetTreeNodes(TreeNodeCollection nodes) {
+      var addedAssignments = newAssignedResources.Except(assignedResources);
+      var removedAssignments = assignedResources.Except(newAssignedResources);
+      var addedIncludes = newIncludedResources.Except(includedResources);
+      var removedIncludes = includedResources.Except(newIncludedResources);
+
+      foreach (TreeNode n in nodes) {
+
+        if(n.Tag is Resource) {
+          // reset
+          var resource = (Resource)n.Tag;
+          n.Text = resource.Name;
+          n.BackColor = Color.Transparent;
+          n.ForeColor = Color.Black;
+          n.Checked = false;
+
+          if (HiveClient.Instance.DisabledParentResources.Select(x => x.Id).Contains(resource.Id) || n == additionalNode) {
+            n.ForeColor = grayTextColor;
+          }
+
+          // add additional info
+          if (newAssignedResources.Select(x => x.Id).Contains(resource.Id)) {
+            n.Checked = true;
+            if (!addedAssignments.Select(x => x.Id).Contains(resource.Id) && !removedAssignments.Select(x => x.Id).Contains(resource.Id)) {
+              n.Text += SELECTED_TAG;
+            }
+          } else if (newIncludedResources.Select(x => x.Id).Contains(resource.Id)) {
+            n.Checked = true;
+            n.ForeColor = grayTextColor;
+          }
+
+          if (includedResources.Select(x => x.Id).Contains(resource.Id) && newIncludedResources.Select(x => x.Id).Contains(resource.Id)) {
+            n.Text += INCLUDED_TAG;
+          } else if (addedIncludes.Select(x => x.Id).Contains(resource.Id)) {
+            n.BackColor = addedIncludeColor;
+            n.ForeColor = grayTextColor;
+            n.Text += ADDED_INCLUDE_TAG;
+          } else if (removedIncludes.Select(x => x.Id).Contains(resource.Id)) {
+            n.BackColor = removedIncludeColor;
+            n.Text += REMOVED_INCLUDE_TAG;
+          }
+
+          if (addedAssignments.Select(x => x.Id).Contains(resource.Id)) {
+            n.BackColor = addedAssignmentColor;
+            n.ForeColor = controlTextColor;
+            n.Text += ADDED_SELECTION_TAG;
+          } else if (removedAssignments.Select(x => x.Id).Contains(resource.Id)) {
+            n.BackColor = removedAssignmentColor;
+            n.ForeColor = controlTextColor;
+            n.Text += REMOVED_SELECTION_TAG;
+          }
+        }
+
+        if(n.Nodes.Count > 0) {
+          SetTreeNodes(n.Nodes);
+        }
+      }
+    }
+
     #endregion
 
-    #region ResourceTree Methods
-    private void UpdateResourceTree() {
-      resourcesTreeView.Nodes.Clear();
-      nodeStore.Clear();
-
-      foreach (TreeNode node in filteredTreeNodes) {
-        var clone = nodeStore.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)node.Tag).Id);
-        if (clone == null) {
-          clone = (TreeNode)node.Clone();
-          nodeStore.Add(clone);
-          clone.Nodes.Clear();
-        }
-        foreach (TreeNode child in node.Nodes)
-          if (filteredTreeNodes.Any(x => ((Resource)x.Tag).Id == ((Resource)child.Tag).Id)) {
-            var childClone = nodeStore.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)child.Tag).Id);
-            if (childClone == null) {
-              childClone = (TreeNode)child.Clone();
-              nodeStore.Add(childClone);
-              childClone.Nodes.Clear();
-            }
-            clone.Nodes.Add(childClone);
-          }
-      }
-      resourcesTreeView.Nodes.AddRange(nodeStore.Where(x => ((Resource)x.Tag).ParentResourceId == null).ToArray());
-      if (string.IsNullOrEmpty(currentSearchString)) ExpandSlaveGroupNodes();
-      else resourcesTreeView.ExpandAll();
-    }
-    #endregion
-
     #region Events
-    private void resourcesTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
-      if (e.Action != TreeViewAction.Unknown) {
-        if (e.Node.Checked) {
-          IncludeChildNodes(mainTreeNodes.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)e.Node.Tag).Id));
-          IncludeParentNodes(mainTreeNodes.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)e.Node.Tag).Id));
-        } else {
-          ExcludeChildNodes(mainTreeNodes.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)e.Node.Tag).Id));
-          ExcludeParentNodes(mainTreeNodes.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)e.Node.Tag).Id));
-        }
-        ExtractStatistics();
-      }
-    }
-
-    private void resourcesTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
-      if (e.Action != TreeViewAction.Unknown) {
-        ExtractStatistics(e.Node);
-      }
-    }
-
-    private void searchTextBox_TextChanged(object sender, System.EventArgs e) {
-      currentSearchString = searchTextBox.Text.ToLower();
-      UpdateFilteredTree();
-    }
-    #endregion
-
-    #region Helpers
-    private void IncludeChildNodes(TreeNode node) {
-      if (node != null) {
-        node.Checked = true;
-        selectedResources.Add((Resource)node.Tag);
-        AdjustNodeCheckedState(node);
-        foreach (TreeNode n in node.Nodes) IncludeChildNodes(n);
-      }
-    }
-
-    private void IncludeParentNodes(TreeNode node) {
-      if (node != null && node.Parent != null) {
-        TreeNode parent = node.Parent;
-        if (parent.Nodes.OfType<TreeNode>().All(x => x.Checked)) {
-          parent.Checked = true;
-          selectedResources.Add((Resource)parent.Tag);
-          AdjustNodeCheckedState(parent);
-          IncludeParentNodes(parent);
-        }
-      }
-    }
-
-    private void ExcludeChildNodes(TreeNode node) {
-      if (node != null) {
-        node.Checked = false;
-        selectedResources.Remove((Resource)node.Tag);
-        AdjustNodeCheckedState(node);
-        foreach (TreeNode n in node.Nodes) ExcludeChildNodes(n);
-      }
-    }
-
-    private void ExcludeParentNodes(TreeNode node) {
-      if (node != null) {
-        node.Checked = false;
-        selectedResources.Remove((Resource)node.Tag);
-        AdjustNodeCheckedState(node);
-        ExcludeParentNodes(node.Parent);
-      }
-    }
-
-    private void AdjustNodeCheckedState(TreeNode node) {
-      var filterdNode = filteredTreeNodes.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)node.Tag).Id);
-      var storedNode = nodeStore.SingleOrDefault(x => ((Resource)x.Tag).Id == ((Resource)node.Tag).Id);
-      if (filterdNode != null) filterdNode.Checked = node.Checked;
-      if (storedNode != null) storedNode.Checked = node.Checked;
-    }
-
-    private void ExpandSlaveGroupNodes() {
-      foreach (TreeNode n in nodeStore.Where(x => x.Tag is SlaveGroup)) {
-        TreeNode[] children = new TreeNode[n.Nodes.Count];
-        n.Nodes.CopyTo(children, 0);
-        if (children.Any(x => x.Tag is SlaveGroup)) n.Expand();
-      }
-    }
-
-    private void ExtractStatistics(TreeNode treeNode = null) {
-      StringBuilder sb = new StringBuilder();
-      Resource resource = treeNode == null ? null : treeNode.Tag as Resource;
-      ISet<Resource> resources = treeNode == null ? selectedResources : new HashSet<Resource>(treeNode.DescendantNodes().Select(x => x.Tag as Resource)); ;
-      IEnumerable<SlaveGroup> slaveGroups = resources.OfType<SlaveGroup>();
-      IEnumerable<Slave> slaves = resources.OfType<Slave>();
-      int cpuSpeed = 0, cores = 0, freeCores = 0, memory = 0, freeMemory = 0;
-      string contextString = treeNode == null ? "Selected" : "Included";
-
-      if (resources.Any() || resource != null) {
-        foreach (Slave s in slaves) {
-          cpuSpeed += s.CpuSpeed.GetValueOrDefault();
-          cores += s.Cores.GetValueOrDefault();
-          freeCores += s.FreeCores.GetValueOrDefault();
-          memory += s.Memory.GetValueOrDefault();
-          freeMemory += s.FreeMemory.GetValueOrDefault();
-        }
-        if (resource != null) {
-          if (resource is SlaveGroup) sb.Append("Slave group: ");
-          else if (resource is Slave) {
-            sb.Append("Slave: ");
-            if (!resources.Any()) {
-              Slave s = resource as Slave;
-              cpuSpeed = s.CpuSpeed.GetValueOrDefault();
-              cores = s.Cores.GetValueOrDefault();
-              freeCores = s.FreeCores.GetValueOrDefault();
-              memory = s.Memory.GetValueOrDefault();
-              freeMemory = s.FreeMemory.GetValueOrDefault();
-            }
-          }
-          sb.AppendLine(string.Format("{0}", resource.Name));
-        }
-        if (resource == null || resource is SlaveGroup) {
-          if (resources.Any()) {
-            sb.AppendFormat("{0} slave groups ({1}): ", contextString, slaveGroups.Count());
-            foreach (SlaveGroup sg in slaveGroups) sb.AppendFormat("{0}; ", sg.Name);
-            sb.AppendLine();
-            sb.AppendFormat("{0} slaves ({1}): ", contextString, slaves.Count());
-            foreach (Slave s in slaves) sb.AppendFormat("{0}; ", s.Name);
-            sb.AppendLine();
-          } else {
-            sb.Append("The selection does not inlcude any further resources.");
-          }
-        }
-        sb.AppendLine();
-        sb.AppendLine(string.Format("CPU speed: {0} MHz", cpuSpeed));
-        if (resources.Any()) sb.AppendLine(string.Format("Avg. CPU speed: {0:0.00} MHz", (double)cpuSpeed / resources.Count()));
-        sb.AppendLine(string.Format("Cores: {0}", cores));
-        sb.AppendLine(string.Format("Free cores: {0}", freeCores));
-        if (resources.Any()) sb.AppendLine(string.Format("Avg. free cores: {0:0.00}", (double)freeCores / resources.Count()));
-        sb.AppendLine(string.Format("Memory: {0} MB", memory));
-        sb.AppendFormat("Free memory: {0} MB", freeMemory);
-        if (resources.Any()) sb.Append(string.Format("{0}Avg. free memory: {1:0.00} MB", Environment.NewLine, (double)freeMemory / resources.Count()));
-      } else {
-        sb.Append("No resources selected.");
-      }
-
-      descriptionTextBox.Text = sb.ToString();
+    public event EventHandler SelectedProjectChanged;
+    private void OnSelectedProjectChanged() {
+      var handler = SelectedProjectChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    public event EventHandler AssignedResourcesChanged;
+    private void OnAssignedResourcesChanged() {
+      var handler = AssignedResourcesChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    public event EventHandler ProjectsTreeViewDoubleClicked;
+    private void OnProjectsTreeViewDoubleClicked() {
+      var handler = ProjectsTreeViewDoubleClicked;
+      if (handler != null) handler(this, EventArgs.Empty);
     }
     #endregion
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelectorDialog.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelectorDialog.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelectorDialog.Designer.cs	(revision 16240)
@@ -45,9 +45,12 @@
     /// </summary>
     private void InitializeComponent() {
-      System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HiveResourceSelectorDialog));
+      this.components = new System.ComponentModel.Container();
       this.okButton = new System.Windows.Forms.Button();
       this.cancelButton = new System.Windows.Forms.Button();
       this.refreshButton = new System.Windows.Forms.Button();
-      this.hiveResourceSelector = new HeuristicLab.Clients.Hive.JobManager.Views.HiveResourceSelector();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
+      this.hiveResourceSelector = new HeuristicLab.Clients.Hive.JobManager.Views.HiveProjectSelector();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
       this.SuspendLayout();
       // 
@@ -56,4 +59,5 @@
       this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
       this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
+      this.okButton.Enabled = false;
       this.okButton.Location = new System.Drawing.Point(405, 609);
       this.okButton.Name = "okButton";
@@ -62,4 +66,6 @@
       this.okButton.Text = "&OK";
       this.okButton.UseVisualStyleBackColor = true;
+      this.errorProvider.SetIconAlignment(this.okButton, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
+      this.errorProvider.SetIconPadding(this.okButton, 2);
       // 
       // cancelButton
@@ -82,24 +88,37 @@
       this.refreshButton.Size = new System.Drawing.Size(24, 24);
       this.refreshButton.TabIndex = 3;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
       this.refreshButton.UseVisualStyleBackColor = true;
       this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
       // 
+      // errorProvider
+      // 
+      this.errorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
+      this.errorProvider.ContainerControl = this;
+      // 
       // hiveResourceSelector
       // 
-      this.hiveResourceSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.hiveResourceSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.hiveResourceSelector.Caption = "View";
       this.hiveResourceSelector.Content = null;
+      this.hiveResourceSelector.JobId = new System.Guid("00000000-0000-0000-0000-000000000000");
       this.hiveResourceSelector.Location = new System.Drawing.Point(12, 12);
       this.hiveResourceSelector.Name = "hiveResourceSelector";
+      this.hiveResourceSelector.ProjectId = null;
       this.hiveResourceSelector.ReadOnly = false;
+      this.hiveResourceSelector.SelectedProject = null;
+      this.hiveResourceSelector.SelectedProjectId = null;
+      this.hiveResourceSelector.SelectedResourceIds = null;
       this.hiveResourceSelector.Size = new System.Drawing.Size(549, 591);
       this.hiveResourceSelector.TabIndex = 0;
+      this.hiveResourceSelector.SelectedProjectChanged += new System.EventHandler(this.hiveResourceSelector_SelectedProjectChanged);
+      this.hiveResourceSelector.AssignedResourcesChanged += new System.EventHandler(this.hiveResourceSelector_SelectedResourcesChanged);
+      this.hiveResourceSelector.ProjectsTreeViewDoubleClicked += new System.EventHandler(this.hiveResourceSelector_ProjectsTreeViewDoubleClicked);
       // 
       // HiveResourceSelectorDialog
       // 
       this.AcceptButton = this.okButton;
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.CancelButton = this.cancelButton;
@@ -115,7 +134,7 @@
       this.ShowInTaskbar = false;
       this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
-      this.Text = "Select Resources";
-      this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.HiveResourceSelectorDialog_FormClosing);
+      this.Text = "Select Project";
       this.Load += new System.EventHandler(this.HiveResourceSelectorDialog_Load);
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
       this.ResumeLayout(false);
 
@@ -126,6 +145,8 @@
     protected System.Windows.Forms.Button okButton;
     protected System.Windows.Forms.Button cancelButton;
-    protected HiveResourceSelector hiveResourceSelector;
     protected System.Windows.Forms.Button refreshButton;
+    protected System.Windows.Forms.ToolTip toolTip;
+    protected System.Windows.Forms.ErrorProvider errorProvider;
+    protected HiveProjectSelector hiveResourceSelector;
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelectorDialog.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelectorDialog.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/HiveResourceSelectorDialog.cs	(revision 16240)
@@ -20,47 +20,161 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
-using System.Threading.Tasks;
+using System.ComponentModel;
+using System.Linq;
 using System.Windows.Forms;
-using HeuristicLab.PluginInfrastructure;
+using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.MainForm;
+using HeuristicLab.Core;
 
 namespace HeuristicLab.Clients.Hive.JobManager.Views {
   public partial class HiveResourceSelectorDialog : Form {
-    public HiveResourceSelectorDialog() {
+    private readonly object locker = new object();
+    private bool updatingProjects = false;
+
+    public Project SelectedProject {
+      get { return hiveResourceSelector.SelectedProject; }
+    }
+
+    public IEnumerable<Resource> SelectedResources {
+      get { return hiveResourceSelector.AssignedResources; }
+    }
+
+    private Guid jobId;
+    public Guid JobId {
+      get { return hiveResourceSelector.JobId; }
+      set { jobId = value; }
+    }
+
+    // persisted projectId
+    private Guid? projectId;
+    public Guid? ProjectId {
+      get { return hiveResourceSelector.ProjectId; }
+      set { projectId = value; }
+    }
+
+    // currently selected projectId (initially the persisted projectId)
+    private Guid? selectedProjectId;
+    public Guid? SelectedProjectId {
+      get { return selectedProjectId; }
+      set { selectedProjectId = value; }
+    }
+
+    // currently selected resourceIds (if null, perform lookup in HiveResourceSelector)
+    private IEnumerable<Guid> selectedResourceIds;
+    public IEnumerable<Guid> SelectedResourceIds {
+      get { return selectedResourceIds; }
+      set { selectedResourceIds = value; }
+    }
+
+
+
+    public HiveResourceSelectorDialog(Guid jobId, Guid projectId) {
+      this.jobId = jobId;
+      this.projectId = projectId;
+      this.selectedProjectId = projectId;
       InitializeComponent();
     }
 
-    public ISet<Resource> GetSelectedResources() { return hiveResourceSelector.SelectedResources; }
-
-    private void HiveResourceSelectorDialog_Load(object sender, System.EventArgs e) {
-      HiveAdminClient.Instance.Refreshed += new System.EventHandler(Instance_Refreshed);
-      DownloadResources();
+    #region Overrides
+    protected override void OnLoad(EventArgs e) {
+      HiveClient.Instance.Refreshing += HiveClient_Instance_Refreshing;
+      HiveClient.Instance.Refreshed += HiveClient_Instance_Refreshed;
+      base.OnLoad(e);
     }
 
-    private void HiveResourceSelectorDialog_FormClosing(object sender, FormClosingEventArgs e) {
-      HiveAdminClient.Instance.Refreshed -= new System.EventHandler(Instance_Refreshed);
+    protected override void OnClosing(CancelEventArgs e) {
+      HiveClient.Instance.Refreshed -= HiveClient_Instance_Refreshed;
+      HiveClient.Instance.Refreshing -= HiveClient_Instance_Refreshing;
+      base.OnClosing(e);
+    }
+    #endregion
+
+    #region Event Handlers
+    private void HiveClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        refreshButton.Enabled = false;
+      }
     }
 
-    void Instance_Refreshed(object sender, System.EventArgs e) {
-      hiveResourceSelector.Content = HiveAdminClient.Instance.Resources;
+    private void HiveClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        refreshButton.Enabled = true;
+      }
     }
 
-    private void refreshButton_Click(object sender, System.EventArgs e) {
-      DownloadResources();
+    private async void HiveResourceSelectorDialog_Load(object sender, EventArgs e) {
+      lock (locker) {
+        if (updatingProjects) return;
+        updatingProjects = true;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateProjects(),
+        finallyCallback: () => updatingProjects = false);
     }
 
-    private void DownloadResources() {
-      var task = System.Threading.Tasks.Task.Factory.StartNew(DownloadResourcesAsync);
-      task.ContinueWith(t => {
-        hiveResourceSelector.FinishProgressView();
-        ErrorHandling.ShowErrorDialog(this, "An error occurred while downloading the tasks.", t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
+    private async void refreshButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (updatingProjects) return;
+        updatingProjects = true;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateProjects(),
+        finallyCallback: () => updatingProjects = false);
     }
 
-    private void DownloadResourcesAsync() {
-      hiveResourceSelector.StartProgressView();
-      HiveAdminClient.Instance.Refresh();
-      hiveResourceSelector.FinishProgressView();
+    private void hiveResourceSelector_SelectedProjectChanged(object sender, EventArgs e) {
+      okButton.Enabled = hiveResourceSelector.SelectedProject != null && hiveResourceSelector.AssignedResources.Any();
     }
+
+    private void hiveResourceSelector_SelectedResourcesChanged(object sender, EventArgs e) {
+      okButton.Enabled = hiveResourceSelector.AssignedResources.Any();
+
+      if(!hiveResourceSelector.AssignedResources.Any()) {
+        errorProvider.SetError(okButton, "Note: currently no resources are assigned");
+      } else if(hiveResourceSelector.AssignedCores == 0) {
+        errorProvider.SetError(okButton, "Note: currently no resources with cores are assigned");
+      } else {
+        errorProvider.SetError(okButton, string.Empty);
+      }
+    }
+
+    private void hiveResourceSelector_ProjectsTreeViewDoubleClicked(object sender, EventArgs e) {
+      if (hiveResourceSelector.SelectedProject == null) return;
+      if (!hiveResourceSelector.AssignedResources.Any()) return;
+
+      DialogResult = DialogResult.OK;
+      Close();
+    }
+    #endregion
+
+    #region Helpers
+    private void UpdateProjects() {
+      HiveClient.Instance.RefreshProjectsAndResources();
+      hiveResourceSelector.JobId = jobId;
+      hiveResourceSelector.ProjectId = projectId;
+      hiveResourceSelector.SelectedProjectId = selectedProjectId;
+      hiveResourceSelector.SelectedResourceIds = selectedResourceIds;
+      hiveResourceSelector.Content = HiveClient.Instance.Projects;
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobListView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobListView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobListView.cs	(revision 16240)
@@ -22,4 +22,5 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading.Tasks;
 using System.Windows.Forms;
@@ -41,14 +42,24 @@
       this.itemsListView.Columns.Add(new ColumnHeader("Date") { Text = "Date" });
       this.itemsListView.Columns.Add(new ColumnHeader("Name") { Text = "Name" });
+      this.itemsListView.Columns.Add(new ColumnHeader("Project") { Text = "Project" });
 
       this.itemsListView.HeaderStyle = ColumnHeaderStyle.Clickable;
       this.itemsListView.FullRowSelect = true;
 
-      this.itemsListView.ListViewItemSorter = new ListViewItemDateComparer(0, SortOrder.Ascending);
+      this.itemsListView.ListViewItemSorter = new ListViewItemComparer(new int[] { 2, 0 }, new SortOrder[] { SortOrder.Ascending, SortOrder.Ascending });      
+
+      foreach (ColumnHeader c in this.itemsListView.Columns) {
+        c.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
+        int w = c.Width;
+        c.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
+        if(w > c.Width) {
+          c.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
+        }
+      }
     }
 
     protected override void SortItemsListView(SortOrder sortOrder) {
       if (itemsListView.Sorting == sortOrder || sortOrder == SortOrder.None) return;
-      ((ListViewItemDateComparer)itemsListView.ListViewItemSorter).Order = sortOrder;
+      ((ListViewItemComparer)itemsListView.ListViewItemSorter).Orders[1] = sortOrder;
       itemsListView.Sorting = sortOrder;
       itemsListView.Sort();
@@ -133,5 +144,10 @@
         base.Content_ItemsAdded(sender, e);
         foreach (ColumnHeader c in this.itemsListView.Columns) {
+          c.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
+          int w = c.Width;
           c.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
+          if (w > c.Width) {
+            c.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
+          }
         }
         foreach (var item in e.Items) {
@@ -180,4 +196,6 @@
       listViewItem.SubItems.Insert(0, new ListViewItem.ListViewSubItem(listViewItem, item.Job.DateCreated.ToString()));
       listViewItem.SubItems.Insert(1, new ListViewItem.ListViewSubItem(listViewItem, item.Job.Name));
+      listViewItem.SubItems.Insert(2, new ListViewItem.ListViewSubItem(listViewItem, HiveClient.Instance.GetProjectAncestry(item.Job.ProjectId)));
+      
       listViewItem.Group = GetListViewGroup(item.Job.OwnerUsername);
       return listViewItem;
@@ -189,4 +207,5 @@
       listViewItem.SubItems[0].Text = item == null ? "null" : item.Job.DateCreated.ToString("dd.MM.yyyy HH:mm");
       listViewItem.SubItems[1].Text = item == null ? "null" : item.Job.Name;
+      listViewItem.SubItems[2].Text = item == null ? "null" : HiveClient.Instance.GetProjectAncestry(item.Job.ProjectId);
       listViewItem.Group = GetListViewGroup(item.Job.OwnerUsername);
       listViewItem.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobView.Designer.cs	(revision 16240)
@@ -63,11 +63,15 @@
       this.executionTimeTextBox = new System.Windows.Forms.TextBox();
       this.pauseButton = new System.Windows.Forms.Button();
-      this.resourceIdsLabel = new System.Windows.Forms.Label();
-      this.resourceNamesTextBox = new System.Windows.Forms.TextBox();
+      this.projectLabel = new System.Windows.Forms.Label();
+      this.projectNameTextBox = new System.Windows.Forms.TextBox();
       this.nameLabel = new System.Windows.Forms.Label();
       this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.descriptionLabel = new System.Windows.Forms.Label();
+      this.descriptionTextBox = new System.Windows.Forms.TextBox();
       this.toolTip = new System.Windows.Forms.ToolTip(this.components);
       this.refreshButton = new System.Windows.Forms.Button();
+      this.updateButton = new System.Windows.Forms.Button();
       this.UnloadButton = new System.Windows.Forms.Button();
+      this.searchButton = new System.Windows.Forms.Button();
       this.refreshAutomaticallyCheckBox = new System.Windows.Forms.CheckBox();
       this.infoGroupBox = new System.Windows.Forms.GroupBox();
@@ -78,5 +82,4 @@
       this.calculatingLabel = new System.Windows.Forms.Label();
       this.jobsLabel = new System.Windows.Forms.Label();
-      this.searchButton = new System.Windows.Forms.Button();
       this.tabControl.SuspendLayout();
       this.tasksTabPage.SuspendLayout();
@@ -91,6 +94,6 @@
       // 
       this.tabControl.AllowDrop = true;
-      this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.tabControl.Controls.Add(this.tasksTabPage);
@@ -99,5 +102,5 @@
       this.tabControl.Controls.Add(this.stateTabPage);
       this.tabControl.Controls.Add(this.logTabPage);
-      this.tabControl.Location = new System.Drawing.Point(0, 78);
+      this.tabControl.Location = new System.Drawing.Point(0, 106);
       this.tabControl.Name = "tabControl";
       this.tabControl.SelectedIndex = 0;
@@ -119,6 +122,6 @@
       // 
       this.jobsTreeView.AllowDrop = true;
-      this.jobsTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.jobsTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.jobsTreeView.Caption = "ItemTree View";
@@ -151,5 +154,5 @@
       this.refreshPermissionsButton.Size = new System.Drawing.Size(24, 24);
       this.refreshPermissionsButton.TabIndex = 23;
-      this.toolTip.SetToolTip(this.refreshPermissionsButton, "Refresh Data");
+      this.toolTip.SetToolTip(this.refreshPermissionsButton, "Refresh data");
       this.refreshPermissionsButton.UseVisualStyleBackColor = true;
       this.refreshPermissionsButton.Click += new System.EventHandler(this.refreshPermissionsButton_Click);
@@ -157,6 +160,6 @@
       // hiveExperimentPermissionListView
       // 
-      this.hiveExperimentPermissionListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.hiveExperimentPermissionListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.hiveExperimentPermissionListView.Caption = "HiveExperimentPermissionList View";
@@ -183,6 +186,6 @@
       // runCollectionViewHost
       // 
-      this.runCollectionViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.runCollectionViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.runCollectionViewHost.Caption = "View";
@@ -209,6 +212,6 @@
       // stateLogViewHost
       // 
-      this.stateLogViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.stateLogViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.stateLogViewHost.Caption = "StateLog View";
@@ -235,6 +238,6 @@
       // logView
       // 
-      this.logView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.logView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.logView.Caption = "Log View";
@@ -250,8 +253,8 @@
       this.startButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
       this.startButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Play;
-      this.startButton.Location = new System.Drawing.Point(0, 536);
+      this.startButton.Location = new System.Drawing.Point(0, 564);
       this.startButton.Name = "startButton";
       this.startButton.Size = new System.Drawing.Size(24, 24);
-      this.startButton.TabIndex = 5;
+      this.startButton.TabIndex = 4;
       this.toolTip.SetToolTip(this.startButton, "Start/Resume Experiment");
       this.startButton.UseVisualStyleBackColor = true;
@@ -262,8 +265,8 @@
       this.stopButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
       this.stopButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Stop;
-      this.stopButton.Location = new System.Drawing.Point(60, 536);
+      this.stopButton.Location = new System.Drawing.Point(60, 564);
       this.stopButton.Name = "stopButton";
       this.stopButton.Size = new System.Drawing.Size(24, 24);
-      this.stopButton.TabIndex = 7;
+      this.stopButton.TabIndex = 6;
       this.toolTip.SetToolTip(this.stopButton, "Stop Experiment");
       this.stopButton.UseVisualStyleBackColor = true;
@@ -274,8 +277,8 @@
       this.executionTimeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
       this.executionTimeLabel.AutoSize = true;
-      this.executionTimeLabel.Location = new System.Drawing.Point(491, 543);
+      this.executionTimeLabel.Location = new System.Drawing.Point(491, 571);
       this.executionTimeLabel.Name = "executionTimeLabel";
       this.executionTimeLabel.Size = new System.Drawing.Size(83, 13);
-      this.executionTimeLabel.TabIndex = 9;
+      this.executionTimeLabel.TabIndex = 100;
       this.executionTimeLabel.Text = "&Execution Time:";
       // 
@@ -283,9 +286,9 @@
       // 
       this.executionTimeTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-      this.executionTimeTextBox.Location = new System.Drawing.Point(580, 540);
+      this.executionTimeTextBox.Location = new System.Drawing.Point(580, 568);
       this.executionTimeTextBox.Name = "executionTimeTextBox";
       this.executionTimeTextBox.ReadOnly = true;
       this.executionTimeTextBox.Size = new System.Drawing.Size(137, 20);
-      this.executionTimeTextBox.TabIndex = 10;
+      this.executionTimeTextBox.TabIndex = 100;
       // 
       // pauseButton
@@ -294,49 +297,69 @@
       this.pauseButton.Enabled = false;
       this.pauseButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Pause;
-      this.pauseButton.Location = new System.Drawing.Point(30, 536);
+      this.pauseButton.Location = new System.Drawing.Point(30, 564);
       this.pauseButton.Name = "pauseButton";
       this.pauseButton.Size = new System.Drawing.Size(24, 24);
-      this.pauseButton.TabIndex = 6;
+      this.pauseButton.TabIndex = 5;
       this.toolTip.SetToolTip(this.pauseButton, "Pause Experiment");
       this.pauseButton.UseVisualStyleBackColor = true;
       this.pauseButton.Click += new System.EventHandler(this.pauseButton_Click);
       // 
-      // resourceIdsLabel
-      // 
-      this.resourceIdsLabel.AutoSize = true;
-      this.resourceIdsLabel.Location = new System.Drawing.Point(3, 56);
-      this.resourceIdsLabel.Name = "resourceIdsLabel";
-      this.resourceIdsLabel.Size = new System.Drawing.Size(61, 13);
-      this.resourceIdsLabel.TabIndex = 12;
-      this.resourceIdsLabel.Text = "Resources:";
-      // 
-      // resourceNamesTextBox
-      // 
-      this.resourceNamesTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.resourceNamesTextBox.Location = new System.Drawing.Point(70, 52);
-      this.resourceNamesTextBox.Name = "resourceNamesTextBox";
-      this.resourceNamesTextBox.Size = new System.Drawing.Size(471, 20);
-      this.resourceNamesTextBox.TabIndex = 2;
-      this.resourceNamesTextBox.Validated += new System.EventHandler(this.resourceNamesTextBox_Validated);
+      // projectLabel
+      // 
+      this.projectLabel.AutoSize = true;
+      this.projectLabel.Location = new System.Drawing.Point(3, 83);
+      this.projectLabel.Name = "projectLabel";
+      this.projectLabel.Size = new System.Drawing.Size(43, 13);
+      this.projectLabel.TabIndex = 100;
+      this.projectLabel.Text = "Project:";
+      // 
+      // projectNameTextBox
+      // 
+      this.projectNameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectNameTextBox.Location = new System.Drawing.Point(100, 80);
+      this.projectNameTextBox.Name = "projectNameTextBox";
+      this.projectNameTextBox.ReadOnly = true;
+      this.projectNameTextBox.Size = new System.Drawing.Size(471, 20);
+      this.projectNameTextBox.TabIndex = 100;
+      this.projectNameTextBox.Validated += new System.EventHandler(this.resourceNamesTextBox_Validated);
       // 
       // nameLabel
       // 
       this.nameLabel.AutoSize = true;
-      this.nameLabel.Location = new System.Drawing.Point(3, 30);
+      this.nameLabel.Location = new System.Drawing.Point(3, 35);
       this.nameLabel.Name = "nameLabel";
       this.nameLabel.Size = new System.Drawing.Size(38, 13);
-      this.nameLabel.TabIndex = 20;
+      this.nameLabel.TabIndex = 100;
       this.nameLabel.Text = "Name:";
       // 
       // nameTextBox
       // 
-      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.nameTextBox.Location = new System.Drawing.Point(70, 27);
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(71, 32);
       this.nameTextBox.Name = "nameTextBox";
-      this.nameTextBox.Size = new System.Drawing.Size(501, 20);
+      this.nameTextBox.Size = new System.Drawing.Size(500, 20);
       this.nameTextBox.TabIndex = 1;
       this.nameTextBox.Validated += new System.EventHandler(this.nameTextBox_Validated);
+      // 
+      // descriptionLabel
+      // 
+      this.descriptionLabel.AutoSize = true;
+      this.descriptionLabel.Location = new System.Drawing.Point(3, 59);
+      this.descriptionLabel.Name = "descriptionLabel";
+      this.descriptionLabel.Size = new System.Drawing.Size(63, 13);
+      this.descriptionLabel.TabIndex = 100;
+      this.descriptionLabel.Text = "Description:";
+      // 
+      // descriptionTextBox
+      // 
+      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.descriptionTextBox.Location = new System.Drawing.Point(71, 56);
+      this.descriptionTextBox.Name = "descriptionTextBox";
+      this.descriptionTextBox.Size = new System.Drawing.Size(500, 20);
+      this.descriptionTextBox.TabIndex = 2;
+      this.descriptionTextBox.Validated += new System.EventHandler(this.descriptionTextBox_Validated);
       // 
       // refreshButton
@@ -346,27 +369,49 @@
       this.refreshButton.Name = "refreshButton";
       this.refreshButton.Size = new System.Drawing.Size(24, 24);
-      this.refreshButton.TabIndex = 22;
+      this.refreshButton.TabIndex = 7;
       this.toolTip.SetToolTip(this.refreshButton, "Refresh Data");
       this.refreshButton.UseVisualStyleBackColor = true;
       this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
       // 
+      // updateButton
+      // 
+      this.updateButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.updateButton.Location = new System.Drawing.Point(30, 0);
+      this.updateButton.Name = "updateButton";
+      this.updateButton.Size = new System.Drawing.Size(24, 24);
+      this.updateButton.TabIndex = 8;
+      this.toolTip.SetToolTip(this.updateButton, "Update Job (Name, Project, Resources)");
+      this.updateButton.UseVisualStyleBackColor = true;
+      this.updateButton.Click += new System.EventHandler(this.updateButton_Click);
+      // 
       // UnloadButton
       // 
       this.UnloadButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Disconnect;
-      this.UnloadButton.Location = new System.Drawing.Point(30, 0);
+      this.UnloadButton.Location = new System.Drawing.Point(57, 0);
       this.UnloadButton.Name = "UnloadButton";
       this.UnloadButton.Size = new System.Drawing.Size(24, 24);
-      this.UnloadButton.TabIndex = 27;
+      this.UnloadButton.TabIndex = 9;
       this.toolTip.SetToolTip(this.UnloadButton, "Unload Job");
       this.UnloadButton.UseVisualStyleBackColor = true;
       this.UnloadButton.Click += new System.EventHandler(this.UnloadButton_Click);
       // 
+      // searchButton
+      // 
+      this.searchButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Zoom;
+      this.searchButton.Location = new System.Drawing.Point(71, 79);
+      this.searchButton.Name = "searchButton";
+      this.searchButton.Size = new System.Drawing.Size(24, 24);
+      this.searchButton.TabIndex = 3;
+      this.toolTip.SetToolTip(this.searchButton, "Select project and resources");
+      this.searchButton.UseVisualStyleBackColor = true;
+      this.searchButton.Click += new System.EventHandler(this.searchButton_Click);
+      // 
       // refreshAutomaticallyCheckBox
       // 
       this.refreshAutomaticallyCheckBox.AutoSize = true;
-      this.refreshAutomaticallyCheckBox.Location = new System.Drawing.Point(70, 3);
+      this.refreshAutomaticallyCheckBox.Location = new System.Drawing.Point(100, 3);
       this.refreshAutomaticallyCheckBox.Name = "refreshAutomaticallyCheckBox";
       this.refreshAutomaticallyCheckBox.Size = new System.Drawing.Size(127, 17);
-      this.refreshAutomaticallyCheckBox.TabIndex = 24;
+      this.refreshAutomaticallyCheckBox.TabIndex = 100;
       this.refreshAutomaticallyCheckBox.Text = "&Refresh automatically";
       this.refreshAutomaticallyCheckBox.UseVisualStyleBackColor = true;
@@ -382,5 +427,5 @@
       this.infoGroupBox.Controls.Add(this.calculatingLabel);
       this.infoGroupBox.Controls.Add(this.jobsLabel);
-      this.infoGroupBox.Location = new System.Drawing.Point(580, 5);
+      this.infoGroupBox.Location = new System.Drawing.Point(578, 16);
       this.infoGroupBox.Name = "infoGroupBox";
       this.infoGroupBox.Size = new System.Drawing.Size(133, 89);
@@ -391,28 +436,28 @@
       // finishedTextBox
       // 
-      this.finishedTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+      this.finishedTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.finishedTextBox.Location = new System.Drawing.Point(74, 64);
       this.finishedTextBox.Name = "finishedTextBox";
       this.finishedTextBox.Size = new System.Drawing.Size(53, 20);
-      this.finishedTextBox.TabIndex = 5;
+      this.finishedTextBox.TabIndex = 100;
       // 
       // calculatingTextBox
       // 
-      this.calculatingTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+      this.calculatingTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.calculatingTextBox.Location = new System.Drawing.Point(74, 40);
       this.calculatingTextBox.Name = "calculatingTextBox";
       this.calculatingTextBox.Size = new System.Drawing.Size(53, 20);
-      this.calculatingTextBox.TabIndex = 4;
+      this.calculatingTextBox.TabIndex = 100;
       // 
       // jobsTextBox
       // 
-      this.jobsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+      this.jobsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.jobsTextBox.Location = new System.Drawing.Point(74, 16);
       this.jobsTextBox.Name = "jobsTextBox";
       this.jobsTextBox.Size = new System.Drawing.Size(53, 20);
-      this.jobsTextBox.TabIndex = 3;
+      this.jobsTextBox.TabIndex = 100;
       // 
       // finishedLabel
@@ -422,5 +467,5 @@
       this.finishedLabel.Name = "finishedLabel";
       this.finishedLabel.Size = new System.Drawing.Size(49, 13);
-      this.finishedLabel.TabIndex = 2;
+      this.finishedLabel.TabIndex = 100;
       this.finishedLabel.Text = "Finished:";
       // 
@@ -431,5 +476,5 @@
       this.calculatingLabel.Name = "calculatingLabel";
       this.calculatingLabel.Size = new System.Drawing.Size(62, 13);
-      this.calculatingLabel.TabIndex = 1;
+      this.calculatingLabel.TabIndex = 100;
       this.calculatingLabel.Text = "Calculating:";
       // 
@@ -440,38 +485,30 @@
       this.jobsLabel.Name = "jobsLabel";
       this.jobsLabel.Size = new System.Drawing.Size(46, 13);
-      this.jobsLabel.TabIndex = 0;
+      this.jobsLabel.TabIndex = 100;
       this.jobsLabel.Text = "Waiting:";
       // 
-      // searchButton
-      // 
-      this.searchButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-      this.searchButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Zoom;
-      this.searchButton.Location = new System.Drawing.Point(547, 50);
-      this.searchButton.Name = "searchButton";
-      this.searchButton.Size = new System.Drawing.Size(24, 24);
-      this.searchButton.TabIndex = 26;
-      this.searchButton.UseVisualStyleBackColor = true;
-      this.searchButton.Click += new System.EventHandler(this.searchButton_Click);
-      // 
       // RefreshableHiveJobView
       // 
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
-      this.Controls.Add(this.UnloadButton);
       this.Controls.Add(this.searchButton);
       this.Controls.Add(this.infoGroupBox);
       this.Controls.Add(this.refreshAutomaticallyCheckBox);
       this.Controls.Add(this.refreshButton);
+      this.Controls.Add(this.updateButton);
+      this.Controls.Add(this.UnloadButton);
       this.Controls.Add(this.nameTextBox);
       this.Controls.Add(this.nameLabel);
+      this.Controls.Add(this.descriptionTextBox);
+      this.Controls.Add(this.descriptionLabel);
       this.Controls.Add(this.tabControl);
       this.Controls.Add(this.startButton);
       this.Controls.Add(this.executionTimeTextBox);
       this.Controls.Add(this.executionTimeLabel);
-      this.Controls.Add(this.resourceNamesTextBox);
+      this.Controls.Add(this.projectNameTextBox);
       this.Controls.Add(this.pauseButton);
-      this.Controls.Add(this.resourceIdsLabel);
+      this.Controls.Add(this.projectLabel);
       this.Controls.Add(this.stopButton);
       this.Name = "RefreshableHiveJobView";
-      this.Size = new System.Drawing.Size(717, 560);
+      this.Size = new System.Drawing.Size(717, 588);
       this.tabControl.ResumeLayout(false);
       this.tasksTabPage.ResumeLayout(false);
@@ -493,6 +530,6 @@
     private System.Windows.Forms.TextBox executionTimeTextBox;
     private System.Windows.Forms.Button pauseButton;
-    private System.Windows.Forms.Label resourceIdsLabel;
-    private System.Windows.Forms.TextBox resourceNamesTextBox;
+    private System.Windows.Forms.Label projectLabel;
+    private System.Windows.Forms.TextBox projectNameTextBox;
     private System.Windows.Forms.TabPage logTabPage;
     private Core.Views.LogView logView;
@@ -501,6 +538,10 @@
     private System.Windows.Forms.Label nameLabel;
     private System.Windows.Forms.TextBox nameTextBox;
+    private System.Windows.Forms.Label descriptionLabel;
+    private System.Windows.Forms.TextBox descriptionTextBox;
     private System.Windows.Forms.ToolTip toolTip;
     private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button updateButton;
+    private System.Windows.Forms.Button UnloadButton;
     private System.Windows.Forms.CheckBox refreshAutomaticallyCheckBox;
     private System.Windows.Forms.GroupBox infoGroupBox;
@@ -520,6 +561,4 @@
     private System.Windows.Forms.Button searchButton;
     private MainForm.WindowsForms.DragOverTabControl tabControl;
-    private System.Windows.Forms.Button UnloadButton;
-
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.JobManager/3.3/Views/RefreshableHiveJobView.cs	(revision 16240)
@@ -23,5 +23,4 @@
 using System.ComponentModel;
 using System.Linq;
-using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
@@ -34,4 +33,5 @@
 using HeuristicLab.Optimization;
 using HeuristicLab.PluginInfrastructure;
+using System.Collections.Generic;
 
 namespace HeuristicLab.Clients.Hive.JobManager.Views {
@@ -45,8 +45,12 @@
     private bool SuppressEvents { get; set; }
     private object runCollectionViewLocker = new object();
+    private Project selectedProject;
+    private Dictionary<Guid, Guid> originalJobProjectAssignment = new Dictionary<Guid, Guid>();
 
     public new RefreshableJob Content {
       get { return (RefreshableJob)base.Content; }
-      set { base.Content = value; }
+      set {
+        base.Content = value;
+      }
     }
 
@@ -117,6 +121,7 @@
         if (Content == null) {
           nameTextBox.Text = string.Empty;
+          descriptionTextBox.Text = string.Empty;
           executionTimeTextBox.Text = string.Empty;
-          resourceNamesTextBox.Text = string.Empty;
+          projectNameTextBox.Text = string.Empty;
           refreshAutomaticallyCheckBox.Checked = false;
           lock (runCollectionViewLocker) {
@@ -128,8 +133,75 @@
           stateLogViewHost.Content = null;
         } else {
+          if(Content.Job != null 
+            && Content.Job.Id != Guid.Empty 
+            && !originalJobProjectAssignment.ContainsKey(Content.Job.Id)) {
+            originalJobProjectAssignment.Add(Content.Job.Id, Content.Job.ProjectId);
+          }
+
+          // project look up
+          if (Content.Job != null && Content.Job.ProjectId == Guid.Empty) {
+            projectNameTextBox.Text = string.Empty;
+            if (HiveClient.Instance != null && HiveClient.Instance.Projects != null && HiveClient.Instance.Projects.Count == 1) {
+              var p = HiveClient.Instance.Projects.FirstOrDefault();
+              if (p != null && p.Id != Guid.Empty) {
+                hiveResourceSelectorDialog = new HiveResourceSelectorDialog(Content.Job.Id, Content.Job.ProjectId);
+                Content.Job.ProjectId = p.Id;
+                var resources = HiveClient.Instance.GetAvailableResourcesForProject(p.Id).ToList();
+                Content.Job.ResourceIds = resources.Select(x => x.Id).ToList();
+                hiveResourceSelectorDialog.SelectedProjectId = Content.Job.ProjectId;
+                hiveResourceSelectorDialog.SelectedResourceIds = Content.Job.ResourceIds;
+
+                var cores = resources.Union(resources.SelectMany(x => HiveClient.Instance.GetAvailableResourceDescendants(x.Id)))
+                  .OfType<Slave>()
+                  .Distinct()
+                  .Sum(x => x.Cores);
+
+                projectNameTextBox.Text = HiveClient.Instance.GetProjectAncestry(Content.Job.ProjectId);
+                projectNameTextBox.Text += "   (" + (cores.HasValue ? cores.Value.ToString() : "0") + " cores)";
+              }                
+            }
+          } else if (Content.Job != null && Content.Job.ProjectId != Guid.Empty) {
+            if (selectedProject == null || selectedProject.Id != Content.Job.ProjectId) {
+              selectedProject = GetProject(Content.Job.ProjectId);
+              hiveResourceSelectorDialog = null;
+            }
+
+            if(hiveResourceSelectorDialog == null)
+              hiveResourceSelectorDialog = new HiveResourceSelectorDialog(Content.Job.Id, Content.Job.ProjectId);
+
+            if (selectedProject != null) {
+              projectNameTextBox.Text = HiveClient.Instance.GetProjectAncestry(selectedProject.Id);
+            } else {
+              projectNameTextBox.Text = string.Empty;
+            }
+
+            List<Resource> resources = null;
+            if (Content.Job.ResourceIds == null)
+              resources = HiveClient.Instance.GetAssignedResourcesForJob(Content.Job.Id).ToList();
+            else
+              resources = HiveClient.Instance.Resources.Where(x => Content.Job.ResourceIds.Contains(x.Id)).ToList();
+
+            Content.Job.ResourceIds = resources.Select(x => x.Id).ToList();
+            hiveResourceSelectorDialog.SelectedResourceIds = Content.Job.ResourceIds;
+
+            var cores = resources.Union(resources.SelectMany(x => HiveClient.Instance.GetAvailableResourceDescendants(x.Id)))
+              .OfType<Slave>()
+              .Distinct()
+              .Sum(x => x.Cores);
+
+            projectNameTextBox.Text += "   (" + (cores.HasValue ? cores.Value.ToString() : "0") + " cores)";
+
+          } else {
+            selectedProject = null;
+            projectNameTextBox.Text = string.Empty;
+            Content.Job.ResourceIds = null;
+          }
+
+
           nameTextBox.Text = Content.Job.Name;
+          descriptionTextBox.Text = Content.Job.Description;
           executionTimeTextBox.Text = Content.ExecutionTime.ToString();
-          resourceNamesTextBox.Text = Content.Job.ResourceNames;
           refreshAutomaticallyCheckBox.Checked = Content.RefreshAutomatically;
+          
           logView.Content = Content.Log;
           lock (runCollectionViewLocker) {
@@ -137,9 +209,8 @@
           }
         }
-      }
-      finally {
+      } finally {
         SuppressEvents = false;
       }
-      hiveExperimentPermissionListView.Content = null; // has to be filled by refresh button
+      hiveExperimentPermissionListView.Content = null; // has to be filled by refresh
       Content_JobStatisticsChanged(this, EventArgs.Empty);
       Content_HiveExperimentChanged(this, EventArgs.Empty);
@@ -158,5 +229,6 @@
       tabControl.Enabled = !Locked;
       nameTextBox.Enabled = !Locked;
-      resourceNamesTextBox.Enabled = !Locked;
+      descriptionTextBox.Enabled = !Locked;
+      projectNameTextBox.Enabled = !Locked;
       searchButton.Enabled = !Locked;
       jobsTreeView.Enabled = !Locked;
@@ -182,11 +254,12 @@
           tabControl.Enabled = !Content.IsProgressing;
 
-          this.nameTextBox.ReadOnly = !Content.IsControllable || Content.ExecutionState != ExecutionState.Prepared || alreadyUploaded || Content.IsProgressing;
-          this.resourceNamesTextBox.ReadOnly = !Content.IsControllable || Content.ExecutionState != ExecutionState.Prepared || alreadyUploaded || Content.IsProgressing;
-          this.searchButton.Enabled = Content.IsControllable && Content.ExecutionState == ExecutionState.Prepared && !alreadyUploaded && !Content.IsProgressing;
+          this.nameTextBox.ReadOnly = Content.IsProgressing;
+          this.descriptionTextBox.ReadOnly = Content.IsProgressing;
+          this.searchButton.Enabled = !Content.IsProgressing && Content.ExecutionState != ExecutionState.Stopped;
           this.jobsTreeView.ReadOnly = !Content.IsControllable || Content.ExecutionState != ExecutionState.Prepared || alreadyUploaded || Content.IsProgressing;
 
           this.refreshAutomaticallyCheckBox.Enabled = Content.IsControllable && alreadyUploaded && jobsLoaded && (Content.ExecutionState == ExecutionState.Started || Content.ExecutionState == ExecutionState.Paused) && !Content.IsProgressing;
           this.refreshButton.Enabled = Content.IsDownloadable && alreadyUploaded && !Content.IsProgressing;
+          this.updateButton.Enabled = Content.ExecutionState != ExecutionState.Prepared && Content.ExecutionState != ExecutionState.Stopped && !Content.IsProgressing;
 
           this.UnloadButton.Enabled = Content.HiveTasks != null && Content.HiveTasks.Count > 0 && alreadyUploaded && !Content.IsProgressing;
@@ -333,15 +406,49 @@
     #region Control events
     private void searchButton_Click(object sender, EventArgs e) {
-      if (hiveResourceSelectorDialog == null)
-        hiveResourceSelectorDialog = new HiveResourceSelectorDialog();
+      if (hiveResourceSelectorDialog == null) {
+        hiveResourceSelectorDialog = new HiveResourceSelectorDialog(Content.Job.Id, Content.Job.ProjectId);
+      } else if(hiveResourceSelectorDialog.JobId != Content.Job.Id) {
+        hiveResourceSelectorDialog.JobId = Content.Job.Id;
+        hiveResourceSelectorDialog.SelectedProjectId = Content.Job.ProjectId;
+        hiveResourceSelectorDialog.SelectedResourceIds = Content.Job.ResourceIds;
+
+        if (originalJobProjectAssignment.ContainsKey(Content.Job.Id)) {
+          hiveResourceSelectorDialog.ProjectId = originalJobProjectAssignment[Content.Job.Id];
+        } else {
+          hiveResourceSelectorDialog.ProjectId = Guid.Empty;
+        }
+      } else if(hiveResourceSelectorDialog.JobId == Guid.Empty && Content.Job.Id == Guid.Empty) {
+        hiveResourceSelectorDialog.JobId = Content.Job.Id;
+        hiveResourceSelectorDialog.ProjectId = Guid.Empty;
+        hiveResourceSelectorDialog.SelectedProjectId = Content.Job.ProjectId;
+        hiveResourceSelectorDialog.SelectedResourceIds = Content.Job.ResourceIds;
+      } else {
+        hiveResourceSelectorDialog.SelectedProjectId = Content.Job.ProjectId;
+        hiveResourceSelectorDialog.SelectedResourceIds = Content.Job.ResourceIds;
+      }
+
       if (hiveResourceSelectorDialog.ShowDialog(this) == DialogResult.OK) {
-        StringBuilder sb = new StringBuilder();
-        foreach (Resource resource in hiveResourceSelectorDialog.GetSelectedResources()) {
-          sb.Append(resource.Name);
-          sb.Append(";");
-        }
-        resourceNamesTextBox.Text = sb.ToString();
-        if (Content.Job.ResourceNames != resourceNamesTextBox.Text)
-          Content.Job.ResourceNames = resourceNamesTextBox.Text;
+        selectedProject = hiveResourceSelectorDialog.SelectedProject;
+        if(selectedProject != null) {       
+          Content.Job.ProjectId = selectedProject.Id;
+          Content.Job.ResourceIds = hiveResourceSelectorDialog.SelectedResources.Select(x => x.Id).ToList();
+
+          var cores = hiveResourceSelectorDialog.SelectedResources
+            .Union(hiveResourceSelectorDialog.SelectedResources
+              .SelectMany(x => HiveClient.Instance.GetAvailableResourceDescendants(x.Id)))
+            .OfType<Slave>()
+            .Distinct()
+            .Sum(x => x.Cores);
+
+          projectNameTextBox.Text = HiveClient.Instance.GetProjectAncestry(selectedProject.Id) + "";
+          projectNameTextBox.Text += "   (" + (cores.HasValue ? cores.Value.ToString() : "0") + " cores)";
+
+        } else {
+          selectedProject = null;
+          projectNameTextBox.Text = string.Empty;
+          Content.Job.ProjectId = Guid.Empty;
+          Content.Job.ResourceIds = null;
+        }
+        SetEnabledStateOfExecutableButtons();
       }
     }
@@ -350,4 +457,8 @@
       if (nameTextBox.Text.Trim() == string.Empty) {
         MessageBox.Show("Please enter a name for the job before uploading it!", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
+      } else if (Content.Job.ProjectId == null || Content.Job.ProjectId == Guid.Empty) {
+        MessageBox.Show("Please select a project before uploading the job!", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
+      } else if (Content.Job.ResourceIds == null || !Content.Job.ResourceIds.Any()) {
+        MessageBox.Show("Please select resources before uploading the job!", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
       } else if (Content.ExecutionState == ExecutionState.Paused) {
         var task = System.Threading.Tasks.Task.Factory.StartNew(ResumeJobAsync, Content);
@@ -359,4 +470,5 @@
       } else {
         HiveClient.StartJob((Exception ex) => ErrorHandling.ShowErrorDialog(this, "Start failed.", ex), Content, new CancellationToken());
+        UpdateSelectorDialog();
       }
     }
@@ -403,7 +515,12 @@
     }
 
+    private void descriptionTextBox_Validated(object sender, EventArgs e) {
+      if (!SuppressEvents && Content.Job != null && Content.Job.Description != descriptionTextBox.Text)
+        Content.Job.Description = descriptionTextBox.Text;
+    }
+
     private void resourceNamesTextBox_Validated(object sender, EventArgs e) {
-      if (!SuppressEvents && Content.Job != null && Content.Job.ResourceNames != resourceNamesTextBox.Text)
-        Content.Job.ResourceNames = resourceNamesTextBox.Text;
+      //if (!SuppressEvents && Content.Job != null && Content.Job.ResourceNames != resourceNamesTextBox.Text)
+      //  Content.Job.ResourceNames = resourceNamesTextBox.Text;
     }
 
@@ -417,102 +534,26 @@
         try {
           invoker.EndInvoke(ar);
-        }
-        catch (Exception ex) {
-          ThreadPool.QueueUserWorkItem(delegate(object exception) { ErrorHandling.ShowErrorDialog(this, (Exception)exception); }, ex);
+        } catch (Exception ex) {
+          ThreadPool.QueueUserWorkItem(delegate (object exception) { ErrorHandling.ShowErrorDialog(this, (Exception)exception); }, ex);
         }
       }, null);
-    }
-
-    private void refreshPermissionsButton_Click(object sender, EventArgs e) {
-      if (this.Content.Job.Id == Guid.Empty) {
-        MessageBox.Show("You have to upload the Job first before you can share it.", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
-      } else {
-        hiveExperimentPermissionListView.Content = HiveClient.GetJobPermissions(this.Content.Job.Id);
-      }
-    }
-    #endregion
-
-    #region Helpers
-    private void SetEnabledStateOfExecutableButtons() {
-      if (Content == null) {
-        startButton.Enabled = pauseButton.Enabled = stopButton.Enabled = false;
-      } else {
-        startButton.Enabled = Content.IsControllable && Content.HiveTasks != null && Content.HiveTasks.Count > 0 && (Content.ExecutionState == ExecutionState.Prepared || Content.ExecutionState == ExecutionState.Paused) && !Content.IsProgressing;
-        pauseButton.Enabled = Content.IsControllable && Content.ExecutionState == ExecutionState.Started && !Content.IsProgressing;
-        stopButton.Enabled = Content.IsControllable && (Content.ExecutionState == ExecutionState.Started || Content.ExecutionState == ExecutionState.Paused) && !Content.IsProgressing;
-      }
-    }
-    #endregion
-
-    #region Drag & Drop
-    private void jobsTreeView_DragOver(object sender, DragEventArgs e) {
-      jobsTreeView_DragEnter(sender, e);
-    }
-
-    private void jobsTreeView_DragEnter(object sender, DragEventArgs e) {
-      e.Effect = DragDropEffects.None;
-      var obj = (IDeepCloneable)e.Data.GetData(Constants.DragDropDataFormat);
-
-      Type objType = obj.GetType();
-      if (ItemTask.IsTypeSupported(objType)) {
-        if (Content.Id != Guid.Empty) e.Effect = DragDropEffects.None;
-        else if ((e.KeyState & 32) == 32) e.Effect = DragDropEffects.Link;  // ALT key
-        else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
-      }
-    }
-
-    private void jobsTreeView_DragDrop(object sender, DragEventArgs e) {
-      if (e.Effect != DragDropEffects.None) {
-        var obj = (IItem)e.Data.GetData(Constants.DragDropDataFormat);
-
-        IItem newObj = null;
-        if (e.Effect.HasFlag(DragDropEffects.Copy)) {
-          newObj = (IItem)obj.Clone();
-        } else {
-          newObj = obj;
-        }
-
-        //IOptimizer and IExecutables need some special care
-        if (newObj is IOptimizer) {
-          ((IOptimizer)newObj).Runs.Clear();
-        }
-        if (newObj is IExecutable) {
-          IExecutable exec = (IExecutable)newObj;
-          if (exec.ExecutionState != ExecutionState.Prepared) {
-            exec.Prepare();
-          }
-        }
-
-        ItemTask hiveTask = ItemTask.GetItemTaskForItem(newObj);
-        Content.HiveTasks.Add(hiveTask.CreateHiveTask());
-      }
-    }
-    #endregion
-
-    private void tabControl_SelectedIndexChanged(object sender, EventArgs e) {
-      if (tabControl.SelectedTab == permissionTabPage) {
-        if (!Content.IsSharable) {
-          MessageBox.Show("Unable to load permissions. You have insufficient access privileges.", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
-          tabControl.SelectedTab = tasksTabPage;
-        }
-      }
-    }
-
-    private RunCollection GetAllRunsFromJob(RefreshableJob job) {
-      if (job != null) {
-        RunCollection runs = new RunCollection() { OptimizerName = job.ItemName };
-
-        foreach (HiveTask subTask in job.HiveTasks) {
-          if (subTask is OptimizerHiveTask) {
-            OptimizerHiveTask ohTask = subTask as OptimizerHiveTask;
-            ohTask.ExecuteReadActionOnItemTask(new Action(delegate() {
-              runs.AddRange(ohTask.ItemTask.Item.Runs);
-            }));
-          }
-        }
-        return runs;
-      } else {
-        return null;
-      }
+      UpdateSelectorDialog();
+    }
+
+    private void updateButton_Click(object sender, EventArgs e) {
+      if (Content.ExecutionState == ExecutionState.Stopped) {
+        MessageBox.Show("Job cannot be updated once it stopped.", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
+        return;
+      }
+
+      var invoker = new Action<RefreshableJob>(HiveClient.UpdateJob);
+      invoker.BeginInvoke(Content, (ar) => {
+        try {
+          invoker.EndInvoke(ar);
+        } catch (Exception ex) {
+          ThreadPool.QueueUserWorkItem(delegate (object exception) { ErrorHandling.ShowErrorDialog(this, (Exception)exception); }, ex);
+        }
+      }, null);
+      UpdateSelectorDialog();
     }
 
@@ -526,4 +567,113 @@
       SetEnabledStateOfControls();
     }
+
+    private void refreshPermissionsButton_Click(object sender, EventArgs e) {
+      if (this.Content.Job.Id == Guid.Empty) {
+        MessageBox.Show("You have to upload the Job first before you can share it.", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
+      } else {
+        hiveExperimentPermissionListView.Content = HiveClient.GetJobPermissions(this.Content.Job.Id);
+      }
+    }
+    #endregion
+
+    #region Helpers
+    private void SetEnabledStateOfExecutableButtons() {
+      if (Content == null) {
+        startButton.Enabled = pauseButton.Enabled = stopButton.Enabled = false;
+      } else {
+        startButton.Enabled = Content.IsControllable && Content.HiveTasks != null && Content.HiveTasks.Count > 0 
+          && Content.Job.ProjectId != Guid.Empty && Content.Job.ResourceIds != null  && Content.Job.ResourceIds.Any() 
+          && (Content.ExecutionState == ExecutionState.Prepared || Content.ExecutionState == ExecutionState.Paused) && !Content.IsProgressing;
+        pauseButton.Enabled = Content.IsControllable && Content.ExecutionState == ExecutionState.Started && !Content.IsProgressing;
+        stopButton.Enabled = Content.IsControllable && (Content.ExecutionState == ExecutionState.Started || Content.ExecutionState == ExecutionState.Paused) && !Content.IsProgressing;
+      }
+    }
+   
+    private Project GetProject(Guid projectId) {
+      return HiveServiceLocator.Instance.CallHiveService(s => s.GetProject(projectId));
+    }
+
+    private void UpdateSelectorDialog() {
+      if(hiveResourceSelectorDialog != null) {
+        hiveResourceSelectorDialog = null;
+        //hiveResourceSelectorDialog.JobId = Content.Job.Id;
+        //hiveResourceSelectorDialog.SelectedProjectId = Content.Job.ProjectId;
+        //hiveResourceSelectorDialog.SelectedResourceIds = Content.Job.ResourceIds;
+      }
+    }
+    #endregion
+
+    #region Drag & Drop
+    private void jobsTreeView_DragOver(object sender, DragEventArgs e) {
+      jobsTreeView_DragEnter(sender, e);
+    }
+
+    private void jobsTreeView_DragEnter(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.None;
+      var obj = (IDeepCloneable)e.Data.GetData(Constants.DragDropDataFormat);
+
+      Type objType = obj.GetType();
+      if (ItemTask.IsTypeSupported(objType)) {
+        if (Content.Id != Guid.Empty) e.Effect = DragDropEffects.None;
+        else if ((e.KeyState & 32) == 32) e.Effect = DragDropEffects.Link;  // ALT key
+        else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
+      }
+    }
+
+    private void jobsTreeView_DragDrop(object sender, DragEventArgs e) {
+      if (e.Effect != DragDropEffects.None) {
+        var obj = (IItem)e.Data.GetData(Constants.DragDropDataFormat);
+
+        IItem newObj = null;
+        if (e.Effect.HasFlag(DragDropEffects.Copy)) {
+          newObj = (IItem)obj.Clone();
+        } else {
+          newObj = obj;
+        }
+
+        //IOptimizer and IExecutables need some special care
+        if (newObj is IOptimizer) {
+          ((IOptimizer)newObj).Runs.Clear();
+        }
+        if (newObj is IExecutable) {
+          IExecutable exec = (IExecutable)newObj;
+          if (exec.ExecutionState != ExecutionState.Prepared) {
+            exec.Prepare();
+          }
+        }
+
+        ItemTask hiveTask = ItemTask.GetItemTaskForItem(newObj);
+        Content.HiveTasks.Add(hiveTask.CreateHiveTask());
+      }
+    }
+    #endregion
+
+    private void tabControl_SelectedIndexChanged(object sender, EventArgs e) {
+      if (tabControl.SelectedTab == permissionTabPage) {
+        if (!Content.IsSharable) {
+          MessageBox.Show("Unable to load permissions. You have insufficient access privileges.", "HeuristicLab Hive Job Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
+          tabControl.SelectedTab = tasksTabPage;
+        }
+      }
+    }
+
+    private RunCollection GetAllRunsFromJob(RefreshableJob job) {
+      if (job != null) {
+        RunCollection runs = new RunCollection() { OptimizerName = job.ItemName };
+
+        foreach (HiveTask subTask in job.HiveTasks) {
+          if (subTask is OptimizerHiveTask) {
+            OptimizerHiveTask ohTask = subTask as OptimizerHiveTask;
+            ohTask.ExecuteReadActionOnItemTask(new Action(delegate () {
+              runs.AddRange(ohTask.ItemTask.Item.Runs);
+            }));
+          }
+        }
+        return runs;
+      } else {
+        return null;
+      }
+    }
+
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Slave.WindowsService/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Slave.WindowsService/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Slave.WindowsService/3.3/app.config	(revision 16240)
@@ -150,5 +150,5 @@
     <client>
       <endpoint name="SlaveCommunicationServiceEndpoint" address="net.pipe://localhost/HeuristicLabSlaveCom" binding="netNamedPipeBinding" contract="HeuristicLab.Clients.Hive.SlaveCore.ServiceContracts.ISlaveCommunication"/>
-      <endpoint address="http://services.heuristiclab.com/Hive-3.3/HiveService.svc"
+      <endpoint address="http://services.heuristiclab.com/Hive-3.4/HiveService.svc"
         binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHiveService"
         contract="HeuristicLab.Clients.Hive.IHiveService" name="WSHttpBinding_IHiveService">
@@ -157,5 +157,5 @@
         </identity>
       </endpoint>
-      <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.3/HiveService.svc"
+      <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.4/HiveService.svc"
         binding="netTcpBinding" bindingConfiguration="NetBinding_IHiveService"
         contract="HeuristicLab.Clients.Hive.IHiveService" name="NetBinding_IHiveService">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Slave/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Slave/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Slave/3.3/app.config	(revision 16240)
@@ -150,5 +150,5 @@
     <client>
       <endpoint name="SlaveCommunicationServiceEndpoint" address="net.pipe://localhost/HeuristicLabSlaveCom" binding="netNamedPipeBinding" contract="HeuristicLab.Clients.Hive.SlaveCore.ServiceContracts.ISlaveCommunication"/>
-      <endpoint address="http://services.heuristiclab.com/Hive-3.3/HiveService.svc"
+      <endpoint address="http://services.heuristiclab.com/Hive-3.4/HiveService.svc"
         binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHiveService"
         contract="HeuristicLab.Clients.Hive.IHiveService" name="WSHttpBinding_IHiveService">
@@ -157,5 +157,5 @@
         </identity>
       </endpoint>
-      <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.3/HiveService.svc"
+      <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.4/HiveService.svc"
         binding="netTcpBinding" bindingConfiguration="NetBinding_IHiveService"
         contract="HeuristicLab.Clients.Hive.IHiveService" name="NetBinding_IHiveService">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/Extensions/FixedNumericUpDown.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/Extensions/FixedNumericUpDown.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/Extensions/FixedNumericUpDown.cs	(revision 16240)
@@ -0,0 +1,40 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Windows.Forms;
+
+namespace HeuristicLab.Clients.Hive.Views.Extensions {
+  // Bugfix for NumericUpDown to disable arrow controls correctly
+  // cf. https://www.codeproject.com/Articles/16321/NumericUpDown-with-correct-read-only-behavior
+  public class FixedNumericUpDown : NumericUpDown {
+    public override void DownButton() {
+      if (ReadOnly)
+        return;
+      base.DownButton();
+    }
+
+    public override void UpButton() {
+      if (ReadOnly)
+        return;
+      base.UpButton();
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HeuristicLab.Clients.Hive.Views-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HeuristicLab.Clients.Hive.Views-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HeuristicLab.Clients.Hive.Views-3.3.csproj	(revision 16240)
@@ -101,4 +101,7 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Extensions\FixedNumericUpDown.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="HiveInformationDialog.cs">
       <SubType>Form</SubType>
@@ -168,4 +171,7 @@
     <Compile Include="StateLog\StateLogView.designer.cs">
       <DependentUpon>StateLogView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="TreeView\NoDoubleClickTreeView.cs">
+      <SubType>Component</SubType>
     </Compile>
     <None Include="Plugin.cs.frame" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveItemView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveItemView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveItemView.Designer.cs	(revision 16240)
@@ -52,5 +52,5 @@
       // storeButton
       // 
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(0, 0);
       this.storeButton.Name = "storeButton";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveTasks/HiveTaskView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveTasks/HiveTaskView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveTasks/HiveTaskView.cs	(revision 16240)
@@ -211,4 +211,12 @@
       }
     }
+
+    private void coresNeededComboBox_SelectedIndexChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Task != null) {
+        int cores;
+        if (int.TryParse(coresNeededComboBox.SelectedItem.ToString(), out cores) && Content.Task.CoresNeeded != cores)
+          Content.Task.CoresNeeded = cores;
+      }
+    }
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveTasks/HiveTaskView.designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveTasks/HiveTaskView.designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/HiveTasks/HiveTaskView.designer.cs	(revision 16240)
@@ -292,9 +292,14 @@
             "2",
             "3",
-            "4"});
+            "4",
+            "6",
+            "8",
+            "10",
+            "12"});
       this.coresNeededComboBox.Location = new System.Drawing.Point(134, 13);
       this.coresNeededComboBox.Name = "coresNeededComboBox";
       this.coresNeededComboBox.Size = new System.Drawing.Size(134, 21);
       this.coresNeededComboBox.TabIndex = 41;
+      this.coresNeededComboBox.SelectedIndexChanged += new System.EventHandler(this.coresNeededComboBox_SelectedIndexChanged);
       // 
       // memoryNeededLabel
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/TreeView/NoDoubleClickTreeView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/TreeView/NoDoubleClickTreeView.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive.Views/3.3/TreeView/NoDoubleClickTreeView.cs	(revision 16240)
@@ -0,0 +1,34 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Windows.Forms;
+
+namespace HeuristicLab.Clients.Hive.Views.TreeView {
+  // Workaround for a well-known bug in TreeView
+  // cf. https://stackoverflow.com/questions/6130297/c-how-to-avoid-treenode-check-from-happening-on-a-double-click-event
+  public class NoDoubleClickTreeView : System.Windows.Forms.TreeView {
+    protected override void WndProc(ref Message m) {
+      // Suppress WM_LBUTTONDBLCLK
+      if (m.Msg == 0x203) { m.Result = IntPtr.Zero; } else base.WndProc(ref m);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HeuristicLab.Clients.Hive-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HeuristicLab.Clients.Hive-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HeuristicLab.Clients.Hive-3.3.csproj	(revision 16240)
@@ -125,4 +125,7 @@
     <Compile Include="HiveAdminClient.cs" />
     <Compile Include="HiveClient.cs" />
+    <Compile Include="ServiceClients\AssignedJobResource.cs" />
+    <Compile Include="ServiceClients\AssignedProjectResource.cs" />
+    <Compile Include="ServiceClients\Project.cs" />
     <Compile Include="ServiceClients\UserPriority.cs" />
     <Compile Include="ServiceClients\ResourcePermission.cs" />
@@ -194,4 +197,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Clients.Access\3.3\HeuristicLab.Clients.Access-3.3.csproj">
+      <Project>{494F87F4-0F25-4D33-A382-10CDB2174D48}</Project>
+      <Name>HeuristicLab.Clients.Access-3.3</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Clients.Common\3.3\HeuristicLab.Clients.Common-3.3.csproj">
       <Project>{730A9104-D4D1-4360-966B-E49B7571DDA3}</Project>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs	(revision 16240)
@@ -24,4 +24,7 @@
 using HeuristicLab.Common;
 using HeuristicLab.Core;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Clients.Access;
 
 namespace HeuristicLab.Clients.Hive {
@@ -36,4 +39,5 @@
     }
 
+    #region Properties
     private IItemList<Resource> resources;
     public IItemList<Resource> Resources {
@@ -57,4 +61,69 @@
     }
 
+    private IItemList<Project> projects;
+    public IItemList<Project> Projects {
+      get { return projects; }
+    }
+
+    private IItemList<AssignedProjectResource> projectResourceAssignments;
+    public IItemList<AssignedProjectResource> ProjectResourceAssignments {
+      get { return projectResourceAssignments; }
+    }
+
+    private Dictionary<Guid, HiveItemCollection<RefreshableJob>> jobs;
+    public Dictionary<Guid, HiveItemCollection<RefreshableJob>> Jobs {
+      get { return jobs; }
+      set {
+        if (value != jobs)
+          jobs = value;
+        }
+    }
+
+    private Dictionary<Guid, List<LightweightTask>> tasks;
+    public Dictionary<Guid, List<LightweightTask>> Tasks {
+      get { return tasks; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> projectAncestors;
+    public Dictionary<Guid, HashSet<Guid>> ProjectAncestors {
+      get { return projectAncestors; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> projectDescendants;
+    public Dictionary<Guid, HashSet<Guid>> ProjectDescendants {
+      get { return projectDescendants; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> resourceAncestors;
+    public Dictionary<Guid, HashSet<Guid>> ResourceAncestors {
+      get { return resourceAncestors; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> resourceDescendants;
+    public Dictionary<Guid, HashSet<Guid>> ResourceDescendants {
+      get { return resourceDescendants; }
+    }
+
+    private Dictionary<Guid, string> projectNames;
+    public Dictionary<Guid, string> ProjectNames {
+      get { return projectNames; }
+    }
+
+    private HashSet<Project> disabledParentProjects;
+    public HashSet<Project> DisabledParentProjects {
+      get { return disabledParentProjects; }
+    }
+
+    private Dictionary<Guid, string> resourceNames;
+    public Dictionary<Guid, string> ResourceNames {
+      get { return resourceNames; }
+    }
+
+    private HashSet<Resource> disabledParentResources;
+    public HashSet<Resource> DisabledParentResources {
+      get { return disabledParentResources; }
+    }
+    #endregion
+
     #region Events
     public event EventHandler Refreshing;
@@ -78,9 +147,44 @@
       try {
         resources = new ItemList<Resource>();
+        projects = new ItemList<Project>();
+        projectResourceAssignments = new ItemList<AssignedProjectResource>();
+        jobs = new Dictionary<Guid, HiveItemCollection<RefreshableJob>>();
+        tasks = new Dictionary<Guid, List<LightweightTask>>();
+        projectNames = new Dictionary<Guid, string>();
+        resourceNames = new Dictionary<Guid, string>();
+
+        projectAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        projectDescendants = new Dictionary<Guid, HashSet<Guid>>();
+        resourceAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        resourceDescendants = new Dictionary<Guid, HashSet<Guid>>();
 
         HiveServiceLocator.Instance.CallHiveService(service => {
-          service.GetSlaveGroups().ForEach(g => resources.Add(g));
-          service.GetSlaves().ForEach(s => resources.Add(s));
+          service.GetSlaveGroupsForAdministration().ForEach(g => resources.Add(g));
+          service.GetSlavesForAdministration().ForEach(s => resources.Add(s));
+          service.GetProjectsForAdministration().ForEach(p => projects.Add(p));
+          var projectIds = projects.Select(p => p.Id).ToList();
+          if (projectIds.Any()) {
+            service.GetAssignedResourcesForProjectsAdministration(projectIds)
+              .ForEach(a => projectResourceAssignments.Add(a));
+            projectIds.ForEach(p => jobs.Add(p, new HiveItemCollection<RefreshableJob>()));
+            var unsortedJobs = service.GetJobsByProjectIds(projectIds)
+              .OrderBy(x => x.DateCreated).ToList();
+
+            unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
+            unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
+            unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
+
+            foreach (var job in jobs.SelectMany(x => x.Value))
+              LoadLightweightJob(job);
+
+            projectNames = service.GetProjectNames();
+            resourceNames = service.GetResourceNames();
+          }
         });
+
+        UpdateResourceGenealogy();
+        UpdateProjectGenealogy();
+        RefreshDisabledParentProjects();
+        RefreshDisabledParentResources();
       }
       catch {
@@ -91,4 +195,199 @@
       }
     }
+
+    //public void UpdateResourceGenealogy(IItemList<Resource> resources) {
+    //  resourceAncestors.Clear();
+    //  resourceDescendants.Clear();
+
+    //  foreach (var r in resources) {
+    //    resourceAncestors.Add(r.Id, new HashSet<Resource>());
+    //    resourceDescendants.Add(r.Id, new HashSet<Resource>());
+    //  }
+
+    //  foreach (var r in resources) {
+    //    var parentResourceId = r.ParentResourceId;
+    //    while (parentResourceId != null) {
+    //      var parent = resources.SingleOrDefault(x => x.Id == parentResourceId);
+    //      if (parent != null) {
+    //        resourceAncestors[r.Id].Add(parent);
+    //        resourceDescendants[parent.Id].Add(r);
+    //        parentResourceId = parent.ParentResourceId;
+    //      } else {
+    //        parentResourceId = null;
+    //      }
+    //    }
+    //  }
+    //}
+
+    //public void UpdateProjectGenealogy(IItemList<Project> projects) {
+    //  projectAncestors.Clear();
+    //  projectDescendants.Clear();
+
+    //  foreach (var p in projects) {
+    //    projectAncestors.Add(p.Id, new HashSet<Project>());
+    //    projectDescendants.Add(p.Id, new HashSet<Project>());
+    //  }
+
+    //  foreach (var p in projects) {
+    //    var parentProjectId = p.ParentProjectId;
+    //    while (parentProjectId != null) {
+    //      var parent = projects.SingleOrDefault(x => x.Id == parentProjectId);
+    //      if (parent != null) {
+    //        projectAncestors[p.Id].Add(parent);
+    //        projectDescendants[parent.Id].Add(p);
+    //        parentProjectId = parent.ParentProjectId;
+    //      } else {
+    //        parentProjectId = null;
+    //      }
+    //    }
+    //  }
+    //}
+
+    private void UpdateResourceGenealogy() {
+      resourceAncestors.Clear();
+      resourceDescendants.Clear();
+
+      // fetch resource ancestor set
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var ra = service.GetResourceGenealogy();
+        ra.Keys.ToList().ForEach(k => resourceAncestors.Add(k, new HashSet<Guid>()));
+        resourceAncestors.Keys.ToList().ForEach(k => resourceAncestors[k].UnionWith(ra[k]));
+      });
+
+      // build resource descendant set
+      resourceAncestors.Keys.ToList().ForEach(k => resourceDescendants.Add(k, new HashSet<Guid>()));
+      foreach (var ra in resourceAncestors) {
+        foreach (var ancestor in ra.Value) {
+          resourceDescendants[ancestor].Add(ra.Key);
+        }
+      }
+    }
+
+    private void UpdateProjectGenealogy() {
+      projectAncestors.Clear();
+      projectDescendants.Clear();
+
+      // fetch project ancestor list
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var pa = service.GetProjectGenealogy();
+        pa.Keys.ToList().ForEach(k => projectAncestors.Add(k, new HashSet<Guid>()));
+        projectAncestors.Keys.ToList().ForEach(k => projectAncestors[k].UnionWith(pa[k]));
+      });
+
+      // build project descendant list
+      projectAncestors.Keys.ToList().ForEach(k => projectDescendants.Add(k, new HashSet<Guid>()));
+      foreach (var pa in projectAncestors) {
+        foreach (var ancestor in pa.Value) {
+          projectDescendants[ancestor].Add(pa.Key);
+        }
+      }
+    }
+
+    private void RefreshDisabledParentProjects() {
+      disabledParentProjects = new HashSet<Project>();
+
+      foreach (var pid in projects
+        .Where(x => x.ParentProjectId.HasValue)
+        .SelectMany(x => projectAncestors[x.Id]).Distinct()
+        .Where(x => !projects.Select(y => y.Id).Contains(x))) {
+        var p = new Project();
+        p.Id = pid;
+        p.ParentProjectId = projectAncestors[pid].FirstOrDefault();
+        p.Name = projectNames[pid];
+        disabledParentProjects.Add(p);
+      }
+    }
+
+    private void RefreshDisabledParentResources() {
+      disabledParentResources = new HashSet<Resource>();
+
+      foreach (var rid in resources
+        .Where(x => x.ParentResourceId.HasValue)
+        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
+        .Where(x => !resources.Select(y => y.Id).Contains(x))) {
+        var r = new SlaveGroup();
+        r.Id = rid;
+        r.ParentResourceId = resourceAncestors[rid].FirstOrDefault();
+        r.Name = resourceNames[rid];
+        disabledParentResources.Add(r);
+      }
+    }
+
+    public void RefreshJobs() {
+      var projectIds = new List<Guid>();
+      jobs = new Dictionary<Guid, HiveItemCollection<RefreshableJob>>();
+      tasks = new Dictionary<Guid, List<LightweightTask>>();
+
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        service.GetProjectsForAdministration().ForEach(p => projectIds.Add(p.Id));
+        if(projectIds.Any()) {
+          projectIds.ForEach(p => jobs.Add(p, new HiveItemCollection<RefreshableJob>()));
+          var unsortedJobs = service.GetJobsByProjectIds(projectIds)
+            .OrderBy(x => x.DateCreated).ToList();
+          
+          unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
+          unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
+          unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
+
+          foreach(var job in jobs.SelectMany(x => x.Value))
+            LoadLightweightJob(job);
+        }
+      });
+    }
+
+    public void LoadLightweightJob(RefreshableJob refreshableJob) {
+      var job = refreshableJob.Job;
+      var lightweightTasks = HiveServiceLocator.Instance.CallHiveService(s => s.GetLightweightJobTasksWithoutStateLog(job.Id));
+
+      if (tasks.ContainsKey(job.Id)) {
+        tasks[job.Id].Clear();
+        tasks[job.Id].AddRange(lightweightTasks);
+      } else {
+        tasks.Add(job.Id, new List<LightweightTask>(lightweightTasks));        
+      }
+
+      if (lightweightTasks != null && lightweightTasks.Count > 0 && lightweightTasks.All(x => x.Id != Guid.Empty)) {
+        if (lightweightTasks.All(x =>
+          x.State == TaskState.Finished 
+          || x.State == TaskState.Aborted 
+          || x.State == TaskState.Failed)) {
+          refreshableJob.ExecutionState = ExecutionState.Stopped;
+          refreshableJob.RefreshAutomatically = false;
+        } else if (
+          lightweightTasks
+            .Where(x => x.ParentTaskId != null)
+            .All(x =>
+              x.State != TaskState.Waiting
+              || x.State != TaskState.Transferring
+              || x.State != TaskState.Calculating)
+          && lightweightTasks
+             .Where(x => x.ParentTaskId != null)
+             .Any(x => x.State == TaskState.Paused)) {
+          refreshableJob.ExecutionState = ExecutionState.Paused;
+          refreshableJob.RefreshAutomatically = false;
+        } else if (lightweightTasks.Any(x => x.State == TaskState.Calculating 
+                                  || x.State == TaskState.Transferring 
+                                  || x.State == TaskState.Waiting)) {
+          refreshableJob.ExecutionState = ExecutionState.Started;
+        }
+
+        refreshableJob.ExecutionTime = TimeSpan.FromMilliseconds(lightweightTasks.Sum(x => x.ExecutionTime.TotalMilliseconds));
+      }
+    }
+
+    public void SortJobs() {
+      for(int i = 0; i < jobs.Count; i++) {
+        var projectId = jobs.Keys.ElementAt(i);
+        var unsortedJobs = jobs.Values.ElementAt(i);
+
+        var sortedJobs = new HiveItemCollection<RefreshableJob>();
+        sortedJobs.AddRange(unsortedJobs.Where(j => j.Job.State == JobState.DeletionPending));
+        sortedJobs.AddRange(unsortedJobs.Where(j => j.Job.State == JobState.StatisticsPending));
+        sortedJobs.AddRange(unsortedJobs.Where(j => j.Job.State == JobState.Online));
+
+        jobs[projectId] = sortedJobs;
+      }
+    }
+
     #endregion
 
@@ -127,4 +426,7 @@
           item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddDowntime((Downtime)item));
         }
+        if (item is Project) {
+          item.Id = HiveServiceLocator.Instance.CallHiveService(s => s.AddProject((Project)item));
+        }
       } else {
         if (item is SlaveGroup) {
@@ -136,4 +438,7 @@
         if (item is Downtime) {
           HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateDowntime((Downtime)item));
+        }
+        if (item is Project) {
+          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateProject((Project)item));
         }
       }
@@ -149,5 +454,54 @@
       } else if (item is Downtime) {
         HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteDowntime(item.Id));
-      }
+      } else if (item is Project) {
+        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteProject(item.Id));
+      }
+    }
+
+    public static void RemoveJobs(List<Guid> jobIds) {
+      HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateJobStates(jobIds, JobState.StatisticsPending));
+    }
+    #endregion
+
+    #region Job Handling
+
+    public static void ResumeJob(RefreshableJob refreshableJob) {
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var tasks = service.GetLightweightJobTasksWithoutStateLog(refreshableJob.Id);
+        foreach (var task in tasks) {
+          if (task.State == TaskState.Paused) {
+            service.RestartTask(task.Id);
+          }
+        }
+      });
+      refreshableJob.ExecutionState = ExecutionState.Started;
+    }
+
+    public static void PauseJob(RefreshableJob refreshableJob) {
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var tasks = service.GetLightweightJobTasksWithoutStateLog(refreshableJob.Id);
+        foreach (var task in tasks) {
+          if (task.State != TaskState.Finished && task.State != TaskState.Aborted && task.State != TaskState.Failed)
+            service.PauseTask(task.Id);
+        }
+      });
+      refreshableJob.ExecutionState = ExecutionState.Paused;
+    }
+
+    public static void StopJob(RefreshableJob refreshableJob) {
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var tasks = service.GetLightweightJobTasksWithoutStateLog(refreshableJob.Id);
+        foreach (var task in tasks) {
+          if (task.State != TaskState.Finished && task.State != TaskState.Aborted && task.State != TaskState.Failed)
+            service.StopTask(task.Id);
+        }
+      });
+      refreshableJob.ExecutionState = ExecutionState.Stopped;
+    }
+
+    public static void RemoveJob(RefreshableJob refreshableJob) {
+      HiveServiceLocator.Instance.CallHiveService((service) => {
+        service.UpdateJobState(refreshableJob.Id, JobState.StatisticsPending);
+      });
     }
     #endregion
@@ -159,4 +513,144 @@
       }
     }
+
+    #region Helper
+    public IEnumerable<Project> GetAvailableProjectAncestors(Guid id) {
+      if (projectAncestors.ContainsKey(id)) return projects.Where(x => projectAncestors[id].Contains(x.Id));
+      else return Enumerable.Empty<Project>();
+    }
+
+    public IEnumerable<Project> GetAvailableProjectDescendants(Guid id) {
+      if(projectDescendants.ContainsKey(id)) return projects.Where(x => projectDescendants[id].Contains(x.Id));
+      else return Enumerable.Empty<Project>();
+    }
+
+    public IEnumerable<Resource> GetAvailableResourceAncestors(Guid id) {
+      if (resourceAncestors.ContainsKey(id)) return resources.Where(x => resourceAncestors[id].Contains(x.Id));
+      else return Enumerable.Empty<Resource>();
+    }
+
+    public IEnumerable<Resource> GetAvailableResourceDescendants(Guid id) {
+      if (resourceDescendants.ContainsKey(id)) return resources.Where(x => resourceDescendants[id].Contains(x.Id));
+      else return Enumerable.Empty<Resource>();
+    }
+
+    public IEnumerable<Resource> GetDisabledResourceAncestors(IEnumerable<Resource> availableResources) {
+      var missingParentIds = availableResources
+        .Where(x => x.ParentResourceId.HasValue)
+        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
+        .Where(x => !availableResources.Select(y => y.Id).Contains(x));
+
+      return resources.OfType<SlaveGroup>().Union(disabledParentResources).Where(x => missingParentIds.Contains(x.Id));
+    }
+
+    public bool CheckAccessToAdminAreaGranted() {
+      if(projects != null) {
+        return projects.Count > 0;
+      } else {
+        bool accessGranted = false;
+        HiveServiceLocator.Instance.CallHiveService(s => {
+          accessGranted = s.CheckAccessToAdminAreaGranted();
+        });
+        return accessGranted;
+      }
+    }
+
+    public bool CheckOwnershipOfResource(Resource res, Guid userId) {
+      if (res == null || userId == Guid.Empty) return false;
+
+      if (res.OwnerUserId == userId) {
+        return true;
+      } else if(resourceAncestors.ContainsKey(res.Id)) {
+        return GetAvailableResourceAncestors(res.Id).Where(x => x.OwnerUserId == userId).Any();
+      }
+
+      return false;
+    }
+
+    public bool CheckOwnershipOfProject(Project pro, Guid userId) {
+      if (pro == null || userId == Guid.Empty) return false;
+
+      if (pro.OwnerUserId == userId) {
+        return true;
+      } else if (projectAncestors.ContainsKey(pro.Id)) {
+        return GetAvailableProjectAncestors(pro.Id).Where(x => x.OwnerUserId == userId).Any();
+      }
+
+      return false;
+    }
+
+    public bool CheckOwnershipOfParentProject(Project pro, Guid userId) {
+      if (pro == null || userId == Guid.Empty) return false;
+
+      if(projectAncestors.ContainsKey(pro.Id)) {
+        return GetAvailableProjectAncestors(pro.Id).Any(x => x.OwnerUserId == userId);
+      }
+
+      if (pro.ParentProjectId != null && pro.ParentProjectId != Guid.Empty) {
+        var parent = projects.FirstOrDefault(x => x.Id == pro.ParentProjectId.Value);
+        if (parent != null)
+          return parent.OwnerUserId == userId || GetAvailableProjectAncestors(parent.Id).Any(x => x.OwnerUserId == userId);
+      }
+
+      return false;
+    }
+
+    public bool CheckParentChange(Project child, Project parent) {
+      bool changePossible = true;
+
+      // change is not possible...
+      // ... if the moved project is null
+      // ... or the new parent is not stored yet
+      // ... or there is not parental change
+      if (child == null
+          || (parent != null && parent.Id == Guid.Empty)
+          || (parent != null && parent.Id == child.ParentProjectId)) {
+        changePossible = false;
+      } else if (parent == null && !IsAdmin()) {
+        // ... if parent is null, but user is no admin (only admins are allowed to create root projects)
+        changePossible = false;
+      } else if (parent != null && (!IsAdmin() && parent.OwnerUserId != UserInformation.Instance.User.Id && !CheckOwnershipOfParentProject(parent, UserInformation.Instance.User.Id))) {
+        // ... if the user is no admin nor owner of the new parent or grand..grandparents
+        changePossible = false;
+      } else if(parent != null && projectDescendants.ContainsKey(child.Id)) {
+        // ... if the new parent is among the moved project's descendants
+        changePossible = !GetAvailableProjectDescendants(child.Id).Where(x => x.Id == parent.Id).Any();
+      }
+
+      return changePossible;
+    }
+
+    public bool CheckParentChange(Resource child, Resource parent) {
+      bool changePossible = true;
+
+      // change is not possisble...
+      // ... if the child resource is null
+      // ... or the child resource equals the parent
+      // ... or the new parent is not stored yet
+      // ... or the new parent is a slave
+      // ... or there is not parental change
+      if (child == null 
+        || child == parent
+        || (parent != null && parent.Id == Guid.Empty)
+        || (parent != null && parent is Slave)
+        || (parent != null && parent.Id == child.ParentResourceId)) {
+        changePossible = false;
+      } else if (parent != null && resourceDescendants.ContainsKey(child.Id)) {
+        // ... or if the new parent is among the moved resource's descendants
+        changePossible = !GetAvailableResourceDescendants(child.Id).Where(x => x.Id == parent.Id).Any();
+      }
+
+      return changePossible;
+    }
+
+    public IEnumerable<Resource> GetAssignedResourcesForJob(Guid jobId) {
+      var assignedJobResource =  HiveServiceLocator.Instance.CallHiveService(service => service.GetAssignedResourcesForJob(jobId));
+      return Resources.Where(x => assignedJobResource.Select(y => y.ResourceId).Contains(x.Id));
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+    #endregion
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HiveClient.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HiveClient.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/HiveClient.cs	(revision 16240)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -57,4 +57,54 @@
     }
 
+    private IItemList<Project> projects;
+    public IItemList<Project> Projects {
+      get { return projects; }
+    }
+
+    private IItemList<Resource> resources;
+    public IItemList<Resource> Resources {
+      get { return resources; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> projectAncestors;
+    public Dictionary<Guid, HashSet<Guid>> ProjectAncestors {
+      get { return projectAncestors; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> projectDescendants;
+    public Dictionary<Guid, HashSet<Guid>> ProjectDescendants {
+      get { return projectDescendants; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> resourceAncestors;
+    public Dictionary<Guid, HashSet<Guid>> ResourceAncestors {
+      get { return resourceAncestors; }
+    }
+
+    private Dictionary<Guid, HashSet<Guid>> resourceDescendants;
+    public Dictionary<Guid, HashSet<Guid>> ResourceDescendants {
+      get { return resourceDescendants; }
+    }
+
+    private Dictionary<Guid, string> projectNames;
+    public Dictionary<Guid, string> ProjectNames {
+      get { return projectNames; }
+    }
+
+    private HashSet<Project> disabledParentProjects;
+    public HashSet<Project> DisabledParentProjects {
+      get { return disabledParentProjects; }
+    }
+
+    private Dictionary<Guid, string> resourceNames;
+    public Dictionary<Guid, string> ResourceNames {
+      get { return resourceNames; }
+    }
+
+    private HashSet<Resource> disabledParentResources;
+    public HashSet<Resource> DisabledParentResources {
+      get { return disabledParentResources; }
+    }
+
     private List<Plugin> onlinePlugins;
     public List<Plugin> OnlinePlugins {
@@ -93,20 +143,193 @@
 
       try {
+        projects = new ItemList<Project>();
+        resources = new ItemList<Resource>();
         jobs = new HiveItemCollection<RefreshableJob>();
-        var jobsLoaded = HiveServiceLocator.Instance.CallHiveService<IEnumerable<Job>>(s => s.GetJobs());
-
-        try {
-          foreach (var j in jobsLoaded) {
-            jobs.Add(new RefreshableJob(j));
-          }
-        } catch (NullReferenceException) {
-          // jobs was set to null during ClearHiveClient
-        }
+        projectNames = new Dictionary<Guid, string>();
+        resourceNames = new Dictionary<Guid, string>();
+
+        projectAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        projectDescendants = new Dictionary<Guid, HashSet<Guid>>();
+        resourceAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        resourceDescendants = new Dictionary<Guid, HashSet<Guid>>();
+
+        HiveServiceLocator.Instance.CallHiveService(service => {
+          service.GetProjects().ForEach(p => projects.Add(p));
+          service.GetSlaveGroups().ForEach(g => resources.Add(g));
+          service.GetSlaves().ForEach(s => resources.Add(s));
+          service.GetJobs().ForEach(p => jobs.Add(new RefreshableJob(p)));
+          projectNames = service.GetProjectNames();
+          resourceNames = service.GetResourceNames();
+        });
+
+        RefreshResourceGenealogy();
+        RefreshProjectGenealogy();
+        RefreshDisabledParentProjects();
+        RefreshDisabledParentResources();
+      }
+      catch {
+        jobs = null;
+        projects = null;
+        resources = null;
+        throw;
+      }
+      finally {
+        OnRefreshed();
+      }
+    }
+
+    public void RefreshProjectsAndResources() {
+      OnRefreshing();
+
+      try {
+        projects = new ItemList<Project>();
+        projectNames = new Dictionary<Guid, string>();
+        resources = new ItemList<Resource>();
+        resourceNames = new Dictionary<Guid, string>();
+
+        projectAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        projectDescendants = new Dictionary<Guid, HashSet<Guid>>();
+        resourceAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        resourceDescendants = new Dictionary<Guid, HashSet<Guid>>();
+
+        HiveServiceLocator.Instance.CallHiveService(service => {
+          service.GetProjects().ForEach(p => projects.Add(p));
+          service.GetSlaveGroups().ForEach(g => resources.Add(g));
+          service.GetSlaves().ForEach(s => resources.Add(s));
+          projectNames = service.GetProjectNames();
+          resourceNames = service.GetResourceNames();
+        });
+
+        RefreshResourceGenealogy();
+        RefreshProjectGenealogy();
+        RefreshDisabledParentProjects();
+        RefreshDisabledParentResources();
       } catch {
-        jobs = null;
+        projects = null;
+        resources = null;
         throw;
       } finally {
         OnRefreshed();
       }
+    }
+
+    public void RefreshAsync(Action<Exception> exceptionCallback) {
+      var call = new Func<Exception>(delegate() {
+        try {
+          Refresh();
+        }
+        catch (Exception ex) {
+          return ex;
+        }
+        return null;
+      });
+      call.BeginInvoke(delegate(IAsyncResult result) {
+        Exception ex = call.EndInvoke(result);
+        if (ex != null) exceptionCallback(ex);
+      }, null);
+    }
+
+    private void RefreshResourceGenealogy() {
+      resourceAncestors.Clear();
+      resourceDescendants.Clear();
+
+      // fetch resource ancestor set
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var ra = service.GetResourceGenealogy();
+        ra.Keys.ToList().ForEach(k => resourceAncestors.Add(k, new HashSet<Guid>()));
+        resourceAncestors.Keys.ToList().ForEach(k => resourceAncestors[k].UnionWith(ra[k]));
+      });
+
+      // build resource descendant set
+      resourceAncestors.Keys.ToList().ForEach(k => resourceDescendants.Add(k, new HashSet<Guid>()));
+      foreach (var ra in resourceAncestors) {
+        foreach(var ancestor in ra.Value) {
+          resourceDescendants[ancestor].Add(ra.Key);
+        }
+      }
+    }
+
+    private void RefreshProjectGenealogy() {
+      projectAncestors.Clear();
+      projectDescendants.Clear();
+
+      // fetch project ancestor list
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        var pa = service.GetProjectGenealogy();
+        pa.Keys.ToList().ForEach(k => projectAncestors.Add(k, new HashSet<Guid>()));
+        projectAncestors.Keys.ToList().ForEach(k => projectAncestors[k].UnionWith(pa[k]));
+      });
+
+      // build project descendant list
+      projectAncestors.Keys.ToList().ForEach(k => projectDescendants.Add(k, new HashSet<Guid>()));
+      foreach(var pa in projectAncestors) {
+        foreach(var ancestor in pa.Value) {
+          projectDescendants[ancestor].Add(pa.Key);
+        }
+      }
+    }
+
+    private void RefreshDisabledParentProjects() {
+      disabledParentProjects = new HashSet<Project>();
+
+      foreach (var pid in projects
+        .Where(x => x.ParentProjectId.HasValue)
+        .SelectMany(x => projectAncestors[x.Id]).Distinct()
+        .Where(x => !projects.Select(y => y.Id).Contains(x))) {
+        var p = new Project();
+        p.Id = pid;
+        p.ParentProjectId = projectAncestors[pid].FirstOrDefault();
+        p.Name = projectNames[pid];
+        disabledParentProjects.Add(p);
+      }
+    }
+
+    private void RefreshDisabledParentResources() {
+      disabledParentResources = new HashSet<Resource>();
+
+      foreach (var rid in resources
+        .Where(x => x.ParentResourceId.HasValue)
+        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
+        .Where(x => !resources.Select(y => y.Id).Contains(x))) {
+        var r = new SlaveGroup();
+        r.Id = rid;
+        r.ParentResourceId = resourceAncestors[rid].FirstOrDefault();
+        r.Name = resourceNames[rid];
+        disabledParentResources.Add(r);
+      }
+    }
+
+    public IEnumerable<Project> GetAvailableProjectAncestors(Guid id) {
+      if (projectAncestors.ContainsKey(id)) return projects.Where(x => projectAncestors[id].Contains(x.Id));
+      else return Enumerable.Empty<Project>();
+    }
+
+    public IEnumerable<Project> GetAvailableProjectDescendants(Guid id) {
+      if (projectDescendants.ContainsKey(id)) return projects.Where(x => projectDescendants[id].Contains(x.Id));
+      else return Enumerable.Empty<Project>();
+    }
+
+    public IEnumerable<Resource> GetAvailableResourceAncestors(Guid id) {
+      if (resourceAncestors.ContainsKey(id)) return resources.Where(x => resourceAncestors[id].Contains(x.Id));
+      else return Enumerable.Empty<Resource>();
+    }
+
+    public IEnumerable<Resource> GetAvailableResourceDescendants(Guid id) {
+      if (resourceDescendants.ContainsKey(id)) return resources.Where(x => resourceDescendants[id].Contains(x.Id));
+      else return Enumerable.Empty<Resource>();
+    }
+
+    public IEnumerable<Resource> GetAvailableResourcesForProject(Guid id) {
+      var assignedProjectResources = HiveServiceLocator.Instance.CallHiveService(s => s.GetAssignedResourcesForProject(id));
+      return resources.Where(x => assignedProjectResources.Select(y => y.ResourceId).Contains(x.Id));
+    }
+
+    public IEnumerable<Resource> GetDisabledResourceAncestors(IEnumerable<Resource> availableResources) {
+      var missingParentIds = availableResources
+        .Where(x => x.ParentResourceId.HasValue)
+        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
+        .Where(x => !availableResources.Select(y => y.Id).Contains(x));
+
+      return resources.OfType<SlaveGroup>().Union(disabledParentResources).Where(x => missingParentIds.Contains(x.Id));
     }
     #endregion
@@ -116,5 +339,5 @@
       if (item.Id == Guid.Empty) {
         if (item is RefreshableJob) {
-          HiveClient.Instance.UploadJob((RefreshableJob)item, cancellationToken);
+          item.Id = HiveClient.Instance.UploadJob((RefreshableJob)item, cancellationToken);
         }
         if (item is JobPermission) {
@@ -126,19 +349,27 @@
           HiveServiceLocator.Instance.CallHiveService((s) => s.GrantPermission(hep.JobId, hep.GrantedUserId, hep.Permission));
         }
+        if (item is Project) {
+          item.Id = HiveServiceLocator.Instance.CallHiveService(s => s.AddProject((Project)item));
+        }
       } else {
-        if (item is Job)
-          HiveServiceLocator.Instance.CallHiveService(s => s.UpdateJob((Job)item));
+        if (item is Job) {
+          var job = (Job)item;
+          HiveServiceLocator.Instance.CallHiveService(s => s.UpdateJob(job, job.ResourceIds));
+        }
+        if (item is Project)
+          HiveServiceLocator.Instance.CallHiveService(s => s.UpdateProject((Project)item));
       }
     }
     public static void StoreAsync(Action<Exception> exceptionCallback, IHiveItem item, CancellationToken cancellationToken) {
-      var call = new Func<Exception>(delegate () {
+      var call = new Func<Exception>(delegate() {
         try {
           Store(item, cancellationToken);
-        } catch (Exception ex) {
+        }
+        catch (Exception ex) {
           return ex;
         }
         return null;
       });
-      call.BeginInvoke(delegate (IAsyncResult result) {
+      call.BeginInvoke(delegate(IAsyncResult result) {
         Exception ex = call.EndInvoke(result);
         if (ex != null) exceptionCallback(ex);
@@ -153,5 +384,5 @@
 
       if (item is Job)
-        HiveServiceLocator.Instance.CallHiveService(s => s.DeleteJob(item.Id));
+        HiveServiceLocator.Instance.CallHiveService(s => s.UpdateJobState(item.Id, JobState.StatisticsPending));
       if (item is RefreshableJob) {
         RefreshableJob job = (RefreshableJob)item;
@@ -159,5 +390,5 @@
           job.StopResultPolling();
         }
-        HiveServiceLocator.Instance.CallHiveService(s => s.DeleteJob(item.Id));
+        HiveServiceLocator.Instance.CallHiveService(s => s.UpdateJobState(item.Id, JobState.StatisticsPending));
       }
       if (item is JobPermission) {
@@ -227,21 +458,39 @@
     }
 
+    public static void UpdateJob(Action<Exception> exceptionCallback, RefreshableJob refreshableJob, CancellationToken cancellationToken) {
+      refreshableJob.IsProgressing = true;
+      refreshableJob.Progress.Status = "Saving Job...";
+      HiveClient.StoreAsync(
+        new Action<Exception>((Exception ex) => {
+          exceptionCallback(ex);
+        }), refreshableJob.Job, cancellationToken);
+      refreshableJob.IsProgressing = false;
+      refreshableJob.Progress.Finish();
+    }
+
+    public static void UpdateJob(RefreshableJob refreshableJob) {
+      refreshableJob.IsProgressing = true;
+
+      try {
+        refreshableJob.Progress.Start("Saving Job...");
+        HiveClient.StoreAsync(new Action<Exception>((Exception ex) => {
+          throw new Exception("Update failed.", ex);
+        }), refreshableJob.Job, new CancellationToken());
+      } finally {
+        refreshableJob.IsProgressing = false;
+        refreshableJob.Progress.Finish();
+      }
+    }
+
+
+
     #region Upload Job
     private Semaphore taskUploadSemaphore = new Semaphore(Settings.Default.MaxParallelUploads, Settings.Default.MaxParallelUploads);
     private static object jobCountLocker = new object();
     private static object pluginLocker = new object();
-    private void UploadJob(RefreshableJob refreshableJob, CancellationToken cancellationToken) {
+    private Guid UploadJob(RefreshableJob refreshableJob, CancellationToken cancellationToken) {
       try {
         refreshableJob.IsProgressing = true;
         refreshableJob.Progress.Start("Connecting to server...");
-        IEnumerable<string> resourceNames = ToResourceNameList(refreshableJob.Job.ResourceNames);
-        var resourceIds = new List<Guid>();
-        foreach (var resourceName in resourceNames) {
-          Guid resourceId = HiveServiceLocator.Instance.CallHiveService((s) => s.GetResourceId(resourceName));
-          if (resourceId == Guid.Empty) {
-            throw new ResourceNotFoundException(string.Format("Could not find the resource '{0}'", resourceName));
-          }
-          resourceIds.Add(resourceId);
-        }
 
         foreach (OptimizerHiveTask hiveJob in refreshableJob.HiveTasks.OfType<OptimizerHiveTask>()) {
@@ -251,5 +500,5 @@
         // upload Job
         refreshableJob.Progress.Status = "Uploading Job...";
-        refreshableJob.Job.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddJob(refreshableJob.Job));
+        refreshableJob.Job.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddJob(refreshableJob.Job, refreshableJob.Job.ResourceIds));
         refreshableJob.Job = HiveServiceLocator.Instance.CallHiveService((s) => s.GetJob(refreshableJob.Job.Id)); // update owner and permissions
         cancellationToken.ThrowIfCancellationRequested();
@@ -273,5 +522,5 @@
         foreach (HiveTask hiveTask in refreshableJob.HiveTasks) {
           var task = TS.Task.Factory.StartNew((hj) => {
-            UploadTaskWithChildren(refreshableJob.Progress, (HiveTask)hj, null, resourceIds, jobCount, totalJobCount, configFilePlugin.Id, refreshableJob.Job.Id, refreshableJob.Log, cancellationToken);
+            UploadTaskWithChildren(refreshableJob.Progress, (HiveTask)hj, null, jobCount, totalJobCount, configFilePlugin.Id, refreshableJob.Job.Id, refreshableJob.Log, cancellationToken);
           }, hiveTask);
           task.ContinueWith((x) => refreshableJob.Log.LogException(x.Exception), TaskContinuationOptions.OnlyOnFaulted);
@@ -279,9 +528,11 @@
         }
         TS.Task.WaitAll(tasks.ToArray());
-      } finally {
+      }
+      finally {
         refreshableJob.Job.Modified = false;
         refreshableJob.IsProgressing = false;
         refreshableJob.Progress.Finish();
       }
+      return (refreshableJob.Job != null) ? refreshableJob.Job.Id : Guid.Empty;
     }
 
@@ -317,5 +568,5 @@
     /// </summary>
     /// <param name="parentHiveTask">shall be null if its the root task</param>
-    private void UploadTaskWithChildren(IProgress progress, HiveTask hiveTask, HiveTask parentHiveTask, IEnumerable<Guid> groups, int[] taskCount, int totalJobCount, Guid configPluginId, Guid jobId, ILog log, CancellationToken cancellationToken) {
+    private void UploadTaskWithChildren(IProgress progress, HiveTask hiveTask, HiveTask parentHiveTask, int[] taskCount, int totalJobCount, Guid configPluginId, Guid jobId, ILog log, CancellationToken cancellationToken) {
       taskUploadSemaphore.WaitOne();
       bool semaphoreReleased = false;
@@ -356,5 +607,5 @@
               hiveTask.Task.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddChildTask(parentHiveTask.Task.Id, hiveTask.Task, taskData));
             } else {
-              hiveTask.Task.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddTask(hiveTask.Task, taskData, groups.ToList()));
+              hiveTask.Task.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddTask(hiveTask.Task, taskData));
             }
           }
@@ -371,5 +622,5 @@
           var task = TS.Task.Factory.StartNew((tuple) => {
             var arguments = (Tuple<HiveTask, HiveTask>)tuple;
-            UploadTaskWithChildren(progress, arguments.Item1, arguments.Item2, groups, taskCount, totalJobCount, configPluginId, jobId, log, cancellationToken);
+            UploadTaskWithChildren(progress, arguments.Item1, arguments.Item2, taskCount, totalJobCount, configPluginId, jobId, log, cancellationToken);
           }, new Tuple<HiveTask, HiveTask>(child, hiveTask));
           task.ContinueWith((x) => log.LogException(x.Exception), TaskContinuationOptions.OnlyOnFaulted);
@@ -426,9 +677,10 @@
         } else if (refreshableJob.IsPaused()) {
           refreshableJob.ExecutionState = Core.ExecutionState.Paused;
-        } else {
+        } else { 
           refreshableJob.ExecutionState = Core.ExecutionState.Started;
         }
         refreshableJob.OnLoaded();
-      } finally {
+      }
+      finally {
         refreshableJob.IsProgressing = false;
         refreshableJob.Progress.Finish();
@@ -467,5 +719,6 @@
       try {
         return PersistenceUtil.Deserialize<ItemTask>(taskData.Data);
-      } catch {
+      }
+      catch {
         return null;
       }
@@ -478,5 +731,6 @@
     public static void TryAndRepeat(Action action, int repetitions, string errorMessage, ILog log = null) {
       while (true) {
-        try { action(); return; } catch (Exception e) {
+        try { action(); return; }
+        catch (Exception e) {
           if (repetitions == 0) throw new HiveException(errorMessage, e);
           if (log != null) log.LogMessage(string.Format("{0}: {1} - will try again!", errorMessage, e.ToString()));
@@ -495,4 +749,16 @@
       });
     }
+
+    public string GetProjectAncestry(Guid projectId) {
+      if (projectId == null || projectId == Guid.Empty) return "";
+      var projects = projectAncestors[projectId].Reverse().ToList();
+      projects.Add(projectId);
+      return string.Join(" » ", projects.Select(x => ProjectNames[x]).ToArray());
+    }
+
+    public IEnumerable<Resource> GetAssignedResourcesForJob(Guid jobId) {
+      var assignedJobResource = HiveServiceLocator.Instance.CallHiveService(service => service.GetAssignedResourcesForJob(jobId));
+      return Resources.Where(x => assignedJobResource.Select(y => y.ResourceId).Contains(x.Id));
+    }
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/Plugin.cs.frame
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/Plugin.cs.frame	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/Plugin.cs.frame	(revision 16240)
@@ -30,4 +30,5 @@
   [PluginFile("HeuristicLab.Clients.Hive-3.3.dll", PluginFileType.Assembly)]
   [PluginDependency("HeuristicLab.Clients.Common", "3.3")]
+  [PluginDependency("HeuristicLab.Clients.Access", "3.3")]  
   [PluginDependency("HeuristicLab.Collections", "3.3")]
   [PluginDependency("HeuristicLab.Common", "3.3")]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/AssignedJobResource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/AssignedJobResource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/AssignedJobResource.cs	(revision 16240)
@@ -0,0 +1,12 @@
+﻿using HeuristicLab.Common;
+
+namespace HeuristicLab.Clients.Hive {
+  public partial class AssignedJobResource : IDeepCloneable, IContent {
+    public AssignedJobResource() { }
+
+    public AssignedJobResource(AssignedJobResource original, Cloner cloner) : base(original, cloner) {
+      ResourceId = original.ResourceId;
+      JobId = original.JobId;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/AssignedProjectResource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/AssignedProjectResource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/AssignedProjectResource.cs	(revision 16240)
@@ -0,0 +1,12 @@
+﻿using HeuristicLab.Common;
+
+namespace HeuristicLab.Clients.Hive {
+  public partial class AssignedProjectResource : IDeepCloneable, IContent {
+    public AssignedProjectResource() { }
+
+    public AssignedProjectResource(AssignedProjectResource original, Cloner cloner) : base(original, cloner) {
+      ResourceId = original.ResourceId;
+      ProjectId = original.ProjectId;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/HiveServiceClient.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/HiveServiceClient.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/HiveServiceClient.cs	(revision 16240)
@@ -26,7 +26,10 @@
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.SlaveGroup))]
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.Plugin))]
+    [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.Project))]
+    [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.AssignedJobResource))]
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.JobPermission))]
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.PluginData))]
-    [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.ResourcePermission))]
+    [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.ProjectPermission))]
+    [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.AssignedProjectResource))]
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.Downtime))]
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.UserPriority))]
@@ -211,4 +214,5 @@
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.SlaveGroup))]
     [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.Plugin))]
+    [System.Runtime.Serialization.KnownTypeAttribute(typeof(HeuristicLab.Clients.Hive.Project))]
     public partial class NamedHiveItem : HeuristicLab.Clients.Hive.HiveItem
     {
@@ -284,5 +288,8 @@
         
         [System.Runtime.Serialization.OptionalFieldAttribute()]
-        private string ResourceNamesField;
+        private System.Guid ProjectIdField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private HeuristicLab.Clients.Hive.JobState StateField;
         
         [System.Runtime.Serialization.DataMemberAttribute()]
@@ -406,16 +413,33 @@
         
         [System.Runtime.Serialization.DataMemberAttribute()]
-        public string ResourceNames
-        {
-            get
-            {
-                return this.ResourceNamesField;
-            }
-            set
-            {
-                if ((object.ReferenceEquals(this.ResourceNamesField, value) != true))
-                {
-                    this.ResourceNamesField = value;
-                    this.RaisePropertyChanged("ResourceNames");
+        public System.Guid ProjectId
+        {
+            get
+            {
+                return this.ProjectIdField;
+            }
+            set
+            {
+                if ((this.ProjectIdField.Equals(value) != true))
+                {
+                    this.ProjectIdField = value;
+                    this.RaisePropertyChanged("ProjectId");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public HeuristicLab.Clients.Hive.JobState State
+        {
+            get
+            {
+                return this.StateField;
+            }
+            set
+            {
+                if ((this.StateField.Equals(value) != true))
+                {
+                    this.StateField = value;
+                    this.RaisePropertyChanged("State");
                 }
             }
@@ -839,4 +863,160 @@
     [System.Diagnostics.DebuggerStepThroughAttribute()]
     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
+    [System.Runtime.Serialization.DataContractAttribute(Name="Project", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
+    [System.SerializableAttribute()]
+    public partial class Project : HeuristicLab.Clients.Hive.NamedHiveItem
+    {
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.DateTime DateCreatedField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Nullable<System.DateTime> EndDateField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Guid OwnerUserIdField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Nullable<System.Guid> ParentProjectIdField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.DateTime StartDateField;
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.DateTime DateCreated
+        {
+            get
+            {
+                return this.DateCreatedField;
+            }
+            set
+            {
+                if ((this.DateCreatedField.Equals(value) != true))
+                {
+                    this.DateCreatedField = value;
+                    this.RaisePropertyChanged("DateCreated");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Nullable<System.DateTime> EndDate
+        {
+            get
+            {
+                return this.EndDateField;
+            }
+            set
+            {
+                if ((this.EndDateField.Equals(value) != true))
+                {
+                    this.EndDateField = value;
+                    this.RaisePropertyChanged("EndDate");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Guid OwnerUserId
+        {
+            get
+            {
+                return this.OwnerUserIdField;
+            }
+            set
+            {
+                if ((this.OwnerUserIdField.Equals(value) != true))
+                {
+                    this.OwnerUserIdField = value;
+                    this.RaisePropertyChanged("OwnerUserId");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Nullable<System.Guid> ParentProjectId
+        {
+            get
+            {
+                return this.ParentProjectIdField;
+            }
+            set
+            {
+                if ((this.ParentProjectIdField.Equals(value) != true))
+                {
+                    this.ParentProjectIdField = value;
+                    this.RaisePropertyChanged("ParentProjectId");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.DateTime StartDate
+        {
+            get
+            {
+                return this.StartDateField;
+            }
+            set
+            {
+                if ((this.StartDateField.Equals(value) != true))
+                {
+                    this.StartDateField = value;
+                    this.RaisePropertyChanged("StartDate");
+                }
+            }
+        }
+    }
+    
+    [System.Diagnostics.DebuggerStepThroughAttribute()]
+    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
+    [System.Runtime.Serialization.DataContractAttribute(Name="AssignedJobResource", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
+    [System.SerializableAttribute()]
+    public partial class AssignedJobResource : HeuristicLab.Clients.Hive.HiveItem
+    {
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Guid JobIdField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Guid ResourceIdField;
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Guid JobId
+        {
+            get
+            {
+                return this.JobIdField;
+            }
+            set
+            {
+                if ((this.JobIdField.Equals(value) != true))
+                {
+                    this.JobIdField = value;
+                    this.RaisePropertyChanged("JobId");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Guid ResourceId
+        {
+            get
+            {
+                return this.ResourceIdField;
+            }
+            set
+            {
+                if ((this.ResourceIdField.Equals(value) != true))
+                {
+                    this.ResourceIdField = value;
+                    this.RaisePropertyChanged("ResourceId");
+                }
+            }
+        }
+    }
+    
+    [System.Diagnostics.DebuggerStepThroughAttribute()]
+    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
     [System.Runtime.Serialization.DataContractAttribute(Name="JobPermission", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
     [System.SerializableAttribute()]
@@ -995,7 +1175,7 @@
     [System.Diagnostics.DebuggerStepThroughAttribute()]
     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
-    [System.Runtime.Serialization.DataContractAttribute(Name="ResourcePermission", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
+    [System.Runtime.Serialization.DataContractAttribute(Name="ProjectPermission", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
     [System.SerializableAttribute()]
-    public partial class ResourcePermission : HeuristicLab.Clients.Hive.HiveItem
+    public partial class ProjectPermission : HeuristicLab.Clients.Hive.HiveItem
     {
         
@@ -1007,5 +1187,5 @@
         
         [System.Runtime.Serialization.OptionalFieldAttribute()]
-        private System.Guid ResourceIdField;
+        private System.Guid ProjectIdField;
         
         [System.Runtime.Serialization.DataMemberAttribute()]
@@ -1039,4 +1219,52 @@
                     this.GrantedUserIdField = value;
                     this.RaisePropertyChanged("GrantedUserId");
+                }
+            }
+        }
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Guid ProjectId
+        {
+            get
+            {
+                return this.ProjectIdField;
+            }
+            set
+            {
+                if ((this.ProjectIdField.Equals(value) != true))
+                {
+                    this.ProjectIdField = value;
+                    this.RaisePropertyChanged("ProjectId");
+                }
+            }
+        }
+    }
+    
+    [System.Diagnostics.DebuggerStepThroughAttribute()]
+    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
+    [System.Runtime.Serialization.DataContractAttribute(Name="AssignedProjectResource", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
+    [System.SerializableAttribute()]
+    public partial class AssignedProjectResource : HeuristicLab.Clients.Hive.HiveItem
+    {
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Guid ProjectIdField;
+        
+        [System.Runtime.Serialization.OptionalFieldAttribute()]
+        private System.Guid ResourceIdField;
+        
+        [System.Runtime.Serialization.DataMemberAttribute()]
+        public System.Guid ProjectId
+        {
+            get
+            {
+                return this.ProjectIdField;
+            }
+            set
+            {
+                if ((this.ProjectIdField.Equals(value) != true))
+                {
+                    this.ProjectIdField = value;
+                    this.RaisePropertyChanged("ProjectId");
                 }
             }
@@ -1612,4 +1840,19 @@
         [System.Runtime.Serialization.EnumMemberAttribute()]
         Full = 2,
+    }
+    
+    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
+    [System.Runtime.Serialization.DataContractAttribute(Name="JobState", Namespace="http://schemas.datacontract.org/2004/07/HeuristicLab.Services.Hive.DataTransfer")]
+    public enum JobState : int
+    {
+        
+        [System.Runtime.Serialization.EnumMemberAttribute()]
+        Online = 0,
+        
+        [System.Runtime.Serialization.EnumMemberAttribute()]
+        StatisticsPending = 1,
+        
+        [System.Runtime.Serialization.EnumMemberAttribute()]
+        DeletionPending = 2,
     }
     
@@ -2100,5 +2343,5 @@
         
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/AddTask", ReplyAction="http://tempuri.org/IHiveService/AddTaskResponse")]
-        System.Guid AddTask(HeuristicLab.Clients.Hive.Task task, HeuristicLab.Clients.Hive.TaskData taskData, System.Collections.Generic.List<System.Guid> resourceIds);
+        System.Guid AddTask(HeuristicLab.Clients.Hive.Task task, HeuristicLab.Clients.Hive.TaskData taskData);
         
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/AddChildTask", ReplyAction="http://tempuri.org/IHiveService/AddChildTaskResponse")]
@@ -2141,12 +2384,24 @@
         System.Collections.Generic.List<HeuristicLab.Clients.Hive.Job> GetJobs();
         
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetJobsByProjectId", ReplyAction="http://tempuri.org/IHiveService/GetJobsByProjectIdResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.Job> GetJobsByProjectId(System.Guid projectId);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetJobsByProjectIds", ReplyAction="http://tempuri.org/IHiveService/GetJobsByProjectIdsResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.Job> GetJobsByProjectIds(System.Collections.Generic.List<System.Guid> projectIds);
+        
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/AddJob", ReplyAction="http://tempuri.org/IHiveService/AddJobResponse")]
-        System.Guid AddJob(HeuristicLab.Clients.Hive.Job jobDto);
+        System.Guid AddJob(HeuristicLab.Clients.Hive.Job jobDto, System.Collections.Generic.List<System.Guid> resourceIds);
         
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/UpdateJob", ReplyAction="http://tempuri.org/IHiveService/UpdateJobResponse")]
-        void UpdateJob(HeuristicLab.Clients.Hive.Job jobDto);
-        
-        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/DeleteJob", ReplyAction="http://tempuri.org/IHiveService/DeleteJobResponse")]
-        void DeleteJob(System.Guid JobId);
+        void UpdateJob(HeuristicLab.Clients.Hive.Job jobDto, System.Collections.Generic.List<System.Guid> resourceIds);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/UpdateJobState", ReplyAction="http://tempuri.org/IHiveService/UpdateJobStateResponse")]
+        void UpdateJobState(System.Guid JobId, HeuristicLab.Clients.Hive.JobState jobState);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/UpdateJobStates", ReplyAction="http://tempuri.org/IHiveService/UpdateJobStatesResponse")]
+        void UpdateJobStates(System.Collections.Generic.List<System.Guid> jobIds, HeuristicLab.Clients.Hive.JobState jobState);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetAssignedResourcesForJob", ReplyAction="http://tempuri.org/IHiveService/GetAssignedResourcesForJobResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedJobResource> GetAssignedResourcesForJob(System.Guid jobId);
         
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GrantPermission", ReplyAction="http://tempuri.org/IHiveService/GrantPermissionResponse")]
@@ -2184,12 +2439,47 @@
         System.Collections.Generic.List<HeuristicLab.Clients.Hive.PluginData> GetPluginDatas(System.Collections.Generic.List<System.Guid> pluginIds);
         
-        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GrantResourcePermissions", ReplyAction="http://tempuri.org/IHiveService/GrantResourcePermissionsResponse")]
-        void GrantResourcePermissions(System.Guid resourceId, System.Collections.Generic.List<System.Guid> grantedUserIds);
-        
-        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/RevokeResourcePermissions", ReplyAction="http://tempuri.org/IHiveService/RevokeResourcePermissionsResponse")]
-        void RevokeResourcePermissions(System.Guid resourceId, System.Collections.Generic.List<System.Guid> grantedUserIds);
-        
-        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetResourcePermissions", ReplyAction="http://tempuri.org/IHiveService/GetResourcePermissionsResponse")]
-        System.Collections.Generic.List<HeuristicLab.Clients.Hive.ResourcePermission> GetResourcePermissions(System.Guid resourceId);
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/AddProject", ReplyAction="http://tempuri.org/IHiveService/AddProjectResponse")]
+        System.Guid AddProject(HeuristicLab.Clients.Hive.Project projectDto);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/UpdateProject", ReplyAction="http://tempuri.org/IHiveService/UpdateProjectResponse")]
+        void UpdateProject(HeuristicLab.Clients.Hive.Project projectDto);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/DeleteProject", ReplyAction="http://tempuri.org/IHiveService/DeleteProjectResponse")]
+        void DeleteProject(System.Guid projectId);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetProject", ReplyAction="http://tempuri.org/IHiveService/GetProjectResponse")]
+        HeuristicLab.Clients.Hive.Project GetProject(System.Guid projectId);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetProjects", ReplyAction="http://tempuri.org/IHiveService/GetProjectsResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.Project> GetProjects();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetProjectsForAdministration", ReplyAction="http://tempuri.org/IHiveService/GetProjectsForAdministrationResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.Project> GetProjectsForAdministration();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetProjectGenealogy", ReplyAction="http://tempuri.org/IHiveService/GetProjectGenealogyResponse")]
+        System.Collections.Generic.Dictionary<System.Guid, System.Collections.Generic.List<System.Guid>> GetProjectGenealogy();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetProjectNames", ReplyAction="http://tempuri.org/IHiveService/GetProjectNamesResponse")]
+        System.Collections.Generic.Dictionary<System.Guid, string> GetProjectNames();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/SaveProjectPermissions", ReplyAction="http://tempuri.org/IHiveService/SaveProjectPermissionsResponse")]
+        void SaveProjectPermissions(System.Guid projectId, System.Collections.Generic.List<System.Guid> grantedUserIds, bool reassign, bool cascading, bool reassignCascading);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetProjectPermissions", ReplyAction="http://tempuri.org/IHiveService/GetProjectPermissionsResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.ProjectPermission> GetProjectPermissions(System.Guid projectId);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/SaveProjectResourceAssignments", ReplyAction="http://tempuri.org/IHiveService/SaveProjectResourceAssignmentsResponse")]
+        void SaveProjectResourceAssignments(System.Guid projectId, System.Collections.Generic.List<System.Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetAssignedResourcesForProject", ReplyAction="http://tempuri.org/IHiveService/GetAssignedResourcesForProjectResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedProjectResource> GetAssignedResourcesForProject(System.Guid projectId);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetAssignedResourcesForProjectAdministration", ReplyAction="http://tempuri.org/IHiveService/GetAssignedResourcesForProjectAdministrationRespo" +
+            "nse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedProjectResource> GetAssignedResourcesForProjectAdministration(System.Guid projectId);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetAssignedResourcesForProjectsAdministration", ReplyAction="http://tempuri.org/IHiveService/GetAssignedResourcesForProjectsAdministrationResp" +
+            "onse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedProjectResource> GetAssignedResourcesForProjectsAdministration(System.Collections.Generic.List<System.Guid> projectIds);
         
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/AddSlave", ReplyAction="http://tempuri.org/IHiveService/AddSlaveResponse")]
@@ -2208,4 +2498,16 @@
         System.Collections.Generic.List<HeuristicLab.Clients.Hive.SlaveGroup> GetSlaveGroups();
         
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetSlavesForAdministration", ReplyAction="http://tempuri.org/IHiveService/GetSlavesForAdministrationResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.Slave> GetSlavesForAdministration();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetSlaveGroupsForAdministration", ReplyAction="http://tempuri.org/IHiveService/GetSlaveGroupsForAdministrationResponse")]
+        System.Collections.Generic.List<HeuristicLab.Clients.Hive.SlaveGroup> GetSlaveGroupsForAdministration();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetResourceGenealogy", ReplyAction="http://tempuri.org/IHiveService/GetResourceGenealogyResponse")]
+        System.Collections.Generic.Dictionary<System.Guid, System.Collections.Generic.List<System.Guid>> GetResourceGenealogy();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetResourceNames", ReplyAction="http://tempuri.org/IHiveService/GetResourceNamesResponse")]
+        System.Collections.Generic.Dictionary<System.Guid, string> GetResourceNames();
+        
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/UpdateSlave", ReplyAction="http://tempuri.org/IHiveService/UpdateSlaveResponse")]
         void UpdateSlave(HeuristicLab.Clients.Hive.Slave slave);
@@ -2252,4 +2554,10 @@
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetUserIdByUsername", ReplyAction="http://tempuri.org/IHiveService/GetUserIdByUsernameResponse")]
         System.Guid GetUserIdByUsername(string username);
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetUserGroupTree", ReplyAction="http://tempuri.org/IHiveService/GetUserGroupTreeResponse")]
+        System.Collections.Generic.Dictionary<System.Guid, System.Collections.Generic.List<System.Guid>> GetUserGroupTree();
+        
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/CheckAccessToAdminAreaGranted", ReplyAction="http://tempuri.org/IHiveService/CheckAccessToAdminAreaGrantedResponse")]
+        bool CheckAccessToAdminAreaGranted();
         
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetUserPriorities", ReplyAction="http://tempuri.org/IHiveService/GetUserPrioritiesResponse")]
@@ -2291,7 +2599,7 @@
         }
         
-        public System.Guid AddTask(HeuristicLab.Clients.Hive.Task task, HeuristicLab.Clients.Hive.TaskData taskData, System.Collections.Generic.List<System.Guid> resourceIds)
-        {
-            return base.Channel.AddTask(task, taskData, resourceIds);
+        public System.Guid AddTask(HeuristicLab.Clients.Hive.Task task, HeuristicLab.Clients.Hive.TaskData taskData)
+        {
+            return base.Channel.AddTask(task, taskData);
         }
         
@@ -2361,17 +2669,37 @@
         }
         
-        public System.Guid AddJob(HeuristicLab.Clients.Hive.Job jobDto)
-        {
-            return base.Channel.AddJob(jobDto);
-        }
-        
-        public void UpdateJob(HeuristicLab.Clients.Hive.Job jobDto)
-        {
-            base.Channel.UpdateJob(jobDto);
-        }
-        
-        public void DeleteJob(System.Guid JobId)
-        {
-            base.Channel.DeleteJob(JobId);
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.Job> GetJobsByProjectId(System.Guid projectId)
+        {
+            return base.Channel.GetJobsByProjectId(projectId);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.Job> GetJobsByProjectIds(System.Collections.Generic.List<System.Guid> projectIds)
+        {
+            return base.Channel.GetJobsByProjectIds(projectIds);
+        }
+        
+        public System.Guid AddJob(HeuristicLab.Clients.Hive.Job jobDto, System.Collections.Generic.List<System.Guid> resourceIds)
+        {
+            return base.Channel.AddJob(jobDto, resourceIds);
+        }
+        
+        public void UpdateJob(HeuristicLab.Clients.Hive.Job jobDto, System.Collections.Generic.List<System.Guid> resourceIds)
+        {
+            base.Channel.UpdateJob(jobDto, resourceIds);
+        }
+        
+        public void UpdateJobState(System.Guid JobId, HeuristicLab.Clients.Hive.JobState jobState)
+        {
+            base.Channel.UpdateJobState(JobId, jobState);
+        }
+        
+        public void UpdateJobStates(System.Collections.Generic.List<System.Guid> jobIds, HeuristicLab.Clients.Hive.JobState jobState)
+        {
+            base.Channel.UpdateJobStates(jobIds, jobState);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedJobResource> GetAssignedResourcesForJob(System.Guid jobId)
+        {
+            return base.Channel.GetAssignedResourcesForJob(jobId);
         }
         
@@ -2431,17 +2759,72 @@
         }
         
-        public void GrantResourcePermissions(System.Guid resourceId, System.Collections.Generic.List<System.Guid> grantedUserIds)
-        {
-            base.Channel.GrantResourcePermissions(resourceId, grantedUserIds);
-        }
-        
-        public void RevokeResourcePermissions(System.Guid resourceId, System.Collections.Generic.List<System.Guid> grantedUserIds)
-        {
-            base.Channel.RevokeResourcePermissions(resourceId, grantedUserIds);
-        }
-        
-        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.ResourcePermission> GetResourcePermissions(System.Guid resourceId)
-        {
-            return base.Channel.GetResourcePermissions(resourceId);
+        public System.Guid AddProject(HeuristicLab.Clients.Hive.Project projectDto)
+        {
+            return base.Channel.AddProject(projectDto);
+        }
+        
+        public void UpdateProject(HeuristicLab.Clients.Hive.Project projectDto)
+        {
+            base.Channel.UpdateProject(projectDto);
+        }
+        
+        public void DeleteProject(System.Guid projectId)
+        {
+            base.Channel.DeleteProject(projectId);
+        }
+        
+        public HeuristicLab.Clients.Hive.Project GetProject(System.Guid projectId)
+        {
+            return base.Channel.GetProject(projectId);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.Project> GetProjects()
+        {
+            return base.Channel.GetProjects();
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.Project> GetProjectsForAdministration()
+        {
+            return base.Channel.GetProjectsForAdministration();
+        }
+        
+        public System.Collections.Generic.Dictionary<System.Guid, System.Collections.Generic.List<System.Guid>> GetProjectGenealogy()
+        {
+            return base.Channel.GetProjectGenealogy();
+        }
+        
+        public System.Collections.Generic.Dictionary<System.Guid, string> GetProjectNames()
+        {
+            return base.Channel.GetProjectNames();
+        }
+        
+        public void SaveProjectPermissions(System.Guid projectId, System.Collections.Generic.List<System.Guid> grantedUserIds, bool reassign, bool cascading, bool reassignCascading)
+        {
+            base.Channel.SaveProjectPermissions(projectId, grantedUserIds, reassign, cascading, reassignCascading);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.ProjectPermission> GetProjectPermissions(System.Guid projectId)
+        {
+            return base.Channel.GetProjectPermissions(projectId);
+        }
+        
+        public void SaveProjectResourceAssignments(System.Guid projectId, System.Collections.Generic.List<System.Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading)
+        {
+            base.Channel.SaveProjectResourceAssignments(projectId, resourceIds, reassign, cascading, reassignCascading);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedProjectResource> GetAssignedResourcesForProject(System.Guid projectId)
+        {
+            return base.Channel.GetAssignedResourcesForProject(projectId);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedProjectResource> GetAssignedResourcesForProjectAdministration(System.Guid projectId)
+        {
+            return base.Channel.GetAssignedResourcesForProjectAdministration(projectId);
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedProjectResource> GetAssignedResourcesForProjectsAdministration(System.Collections.Generic.List<System.Guid> projectIds)
+        {
+            return base.Channel.GetAssignedResourcesForProjectsAdministration(projectIds);
         }
         
@@ -2471,4 +2854,24 @@
         }
         
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.Slave> GetSlavesForAdministration()
+        {
+            return base.Channel.GetSlavesForAdministration();
+        }
+        
+        public System.Collections.Generic.List<HeuristicLab.Clients.Hive.SlaveGroup> GetSlaveGroupsForAdministration()
+        {
+            return base.Channel.GetSlaveGroupsForAdministration();
+        }
+        
+        public System.Collections.Generic.Dictionary<System.Guid, System.Collections.Generic.List<System.Guid>> GetResourceGenealogy()
+        {
+            return base.Channel.GetResourceGenealogy();
+        }
+        
+        public System.Collections.Generic.Dictionary<System.Guid, string> GetResourceNames()
+        {
+            return base.Channel.GetResourceNames();
+        }
+        
         public void UpdateSlave(HeuristicLab.Clients.Hive.Slave slave)
         {
@@ -2546,4 +2949,14 @@
         }
         
+        public System.Collections.Generic.Dictionary<System.Guid, System.Collections.Generic.List<System.Guid>> GetUserGroupTree()
+        {
+            return base.Channel.GetUserGroupTree();
+        }
+        
+        public bool CheckAccessToAdminAreaGranted()
+        {
+            return base.Channel.CheckAccessToAdminAreaGranted();
+        }
+        
         public System.Collections.Generic.List<HeuristicLab.Clients.Hive.UserPriority> GetUserPriorities()
         {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/Job.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/Job.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/Job.cs	(revision 16240)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
 using HeuristicLab.Common;
@@ -26,8 +27,9 @@
 namespace HeuristicLab.Clients.Hive {
   public partial class Job : IDeepCloneable, IContent {
+    public List<Guid> ResourceIds { get; set; }
 
     #region Constructors and Cloning
     public Job() {
-      ResourceNames = "HEAL";
+      ProjectId = Guid.Empty;
       DateCreated = DateTime.Now;
       Permission = Permission.Full;
@@ -38,9 +40,10 @@
       this.OwnerUserId = original.OwnerUserId;
       this.DateCreated = original.DateCreated;
-      this.ResourceNames = original.ResourceNames;
+      this.ProjectId = original.ProjectId;
       this.Name = original.Name;
       this.Description = original.Description;
       this.Id = original.Id;
       this.Permission = original.Permission;
+      this.ResourceIds = original.ResourceIds;
     }
     public override IDeepCloneable Clone(Cloner cloner) {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/Project.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/Project.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/Project.cs	(revision 16240)
@@ -0,0 +1,42 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Common;
+
+namespace HeuristicLab.Clients.Hive {
+  public partial class Project : IDeepCloneable, IContent {
+    public Project() {
+      Description = string.Empty;
+      DateCreated = DateTime.Now;
+      StartDate = DateCreated;
+      EndDate = DateCreated.AddMonths(3);
+    }
+
+    protected Project(Project original, Cloner cloner) : base(original, cloner) {
+      ParentProjectId = original.ParentProjectId;
+      DateCreated = original.DateCreated;
+      OwnerUserId = original.OwnerUserId;
+      StartDate = original.StartDate;
+      EndDate = original.EndDate;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/ResourcePermission.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/ResourcePermission.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/ServiceClients/ResourcePermission.cs	(revision 16240)
@@ -24,5 +24,5 @@
 
 namespace HeuristicLab.Clients.Hive {
-  public partial class ResourcePermission : IDeepCloneable, IContent {
+  public partial class ProjectPermission : IDeepCloneable, IContent {
 
     private string grantedUserName;
@@ -41,5 +41,5 @@
     }
 
-    public ResourcePermission() { }
+    public ProjectPermission() { }
 
     protected override void OnPropertyChanged(PropertyChangedEventArgs e) {
@@ -50,13 +50,13 @@
     }
 
-    protected ResourcePermission(ResourcePermission original, Cloner cloner)
+    protected ProjectPermission(ProjectPermission original, Cloner cloner)
       : base(original, cloner) {
       this.GrantedByUserId = original.GrantedByUserId;
       this.GrantedUserId = original.GrantedUserId;
-      this.ResourceId = original.ResourceId;
+      this.ProjectId = original.ProjectId;
     }
 
     public override IDeepCloneable Clone(Cloner cloner) {
-      return new ResourcePermission(this, cloner);
+      return new ProjectPermission(this, cloner);
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.Hive/3.3/app.config	(revision 16240)
@@ -71,5 +71,5 @@
     </bindings>
     <client>
-      <endpoint address="http://services.heuristiclab.com/Hive-3.3/HiveService.svc"
+      <endpoint address="http://services.heuristiclab.com/Hive-3.4/HiveService.svc"
         binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHiveService"
         contract="HeuristicLab.Clients.Hive.IHiveService" name="WSHttpBinding_IHiveService">
@@ -78,5 +78,5 @@
         </identity>
       </endpoint>
-      <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.3/HiveService.svc"
+      <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.4/HiveService.svc"
         binding="netTcpBinding" bindingConfiguration="NetBinding_IHiveService"
         contract="HeuristicLab.Clients.Hive.IHiveService" name="NetBinding_IHiveService">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/AlgorithmView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/AlgorithmView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/AlgorithmView.Designer.cs	(revision 16240)
@@ -42,5 +42,5 @@
       this.usersTabPage = new System.Windows.Forms.TabPage();
       this.storeUsersButton = new System.Windows.Forms.Button();
-      this.refreshableLightweightUserView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
+      this.refreshableLightweightUserView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightAccessClientView();
       this.dataTabPage = new System.Windows.Forms.TabPage();
       this.noViewAvailableLabel = new System.Windows.Forms.Label();
@@ -147,5 +147,5 @@
       // storeUsersButton
       // 
-      this.storeUsersButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeUsersButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeUsersButton.Location = new System.Drawing.Point(37, 9);
       this.storeUsersButton.Name = "storeUsersButton";
@@ -250,5 +250,5 @@
       // storeDataButton
       // 
-      this.storeDataButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeDataButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeDataButton.Location = new System.Drawing.Point(36, 6);
       this.storeDataButton.Name = "storeDataButton";
@@ -394,5 +394,5 @@
     private MainForm.WindowsForms.ViewHost dataViewHost;
     private System.Windows.Forms.Label noViewAvailableLabel;
-    private Access.Views.RefreshableLightweightUserView refreshableLightweightUserView;
+    private Access.Views.RefreshableLightweightAccessClientView refreshableLightweightUserView;
 
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/OKBItemView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/OKBItemView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/OKBItemView.Designer.cs	(revision 16240)
@@ -52,5 +52,5 @@
       // storeButton
       // 
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(0, 0);
       this.storeButton.Name = "storeButton";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/ProblemView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/ProblemView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/Administration/Views/ProblemView.Designer.cs	(revision 16240)
@@ -42,5 +42,5 @@
       this.usersTabPage = new System.Windows.Forms.TabPage();
       this.storeUsersButton = new System.Windows.Forms.Button();
-      this.problemUserView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
+      this.problemUserView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightAccessClientView();
       this.dataTabPage = new System.Windows.Forms.TabPage();
       this.noViewAvailableLabel = new System.Windows.Forms.Label();
@@ -147,5 +147,5 @@
       // storeUsersButton
       // 
-      this.storeUsersButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeUsersButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeUsersButton.Location = new System.Drawing.Point(38, 9);
       this.storeUsersButton.Name = "storeUsersButton";
@@ -249,5 +249,5 @@
       // storeDataButton
       // 
-      this.storeDataButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeDataButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeDataButton.Location = new System.Drawing.Point(36, 6);
       this.storeDataButton.Name = "storeDataButton";
@@ -393,5 +393,5 @@
     private MainForm.WindowsForms.ViewHost dataViewHost;
     private System.Windows.Forms.Label noViewAvailableLabel;
-    private Access.Views.RefreshableLightweightUserView problemUserView;
+    private Access.Views.RefreshableLightweightAccessClientView problemUserView;
 
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/CreateFromExperimentMenuItem.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/CreateFromExperimentMenuItem.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/CreateFromExperimentMenuItem.cs	(revision 16240)
@@ -41,5 +41,8 @@
     protected override void OnActiveViewChanged(object sender, EventArgs e) {
       IContentView activeView = MainFormManager.MainForm.ActiveView as IContentView;
-      ToolStripItem.Enabled = (activeView != null) && (activeView.Content != null) && ((activeView.Content is Experiment) || (activeView.Content is RunCollection) || (activeView.Content is IOptimizer)) && !activeView.Locked && OKBRoles.CheckUserPermissions();
+
+      ToolStripItem.Enabled = (activeView != null) && (activeView.Content != null)
+        && ((activeView.Content is Experiment) || (activeView.Content is RunCollection) || (activeView.Content is IOptimizer))
+        && !activeView.Locked;
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/OKBProblemView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/OKBProblemView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/OKBProblemView.cs	(revision 16240)
@@ -72,9 +72,9 @@
       downloadCharacteristicsButton.Image = VSImageLibrary.Refresh;
       uploadCharacteristicsButton.Text = string.Empty;
-      uploadCharacteristicsButton.Image = VSImageLibrary.PublishToWeb;
+      uploadCharacteristicsButton.Image = VSImageLibrary.Save;
       refreshSolutionsButton.Text = string.Empty;
       refreshSolutionsButton.Image = VSImageLibrary.Refresh;
       uploadSolutionsButton.Text = string.Empty;
-      uploadSolutionsButton.Image = VSImageLibrary.PublishToWeb;
+      uploadSolutionsButton.Image = VSImageLibrary.Save;
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/OKBRunView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/OKBRunView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/OKBRunView.Designer.cs	(revision 16240)
@@ -51,5 +51,5 @@
       // storeButton
       // 
-      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.storeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.storeButton.Location = new System.Drawing.Point(0, 0);
       this.storeButton.Name = "storeButton";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/SingleObjectiveOKBSolutionView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/SingleObjectiveOKBSolutionView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Clients.OKB.Views/3.3/RunCreation/Views/SingleObjectiveOKBSolutionView.cs	(revision 16240)
@@ -41,5 +41,5 @@
       refreshButton.Image = VSImageLibrary.Refresh;
       uploadButton.Text = string.Empty;
-      uploadButton.Image = VSImageLibrary.PublishToWeb;
+      uploadButton.Image = VSImageLibrary.Save;
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs.sln
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs.sln	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs.sln	(revision 16240)
@@ -67,7 +67,7 @@
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.EPPlus-4.0.3", "HeuristicLab.ExtLibs\HeuristicLab.EPPlus\4.0.3\HeuristicLab.EPPlus-4.0.3\HeuristicLab.EPPlus-4.0.3.csproj", "{675A6EE4-DE7C-413E-8BEF-EB21E0384C87}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimSharp-3.0.9", "HeuristicLab.ExtLibs\HeuristicLab.SimSharp\3.0.9\SimSharp-3.0.9\SimSharp-3.0.9.csproj", "{9FE9C740-7859-4D01-AD07-7D4E15A6320B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.SimSharp-3.0.9", "HeuristicLab.ExtLibs\HeuristicLab.SimSharp\3.0.9\HeuristicLab.SimSharp-3.0.9\HeuristicLab.SimSharp-3.0.9.csproj", "{5C93CAF7-AE6A-4540-95B2-5DA6761A2677}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimSharp-3.0.11", "HeuristicLab.ExtLibs\HeuristicLab.SimSharp\3.0.11\SimSharp-3.0.11\SimSharp-3.0.11.csproj", "{9FE9C740-7859-4D01-AD07-7D4E15A6320B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.SimSharp-3.0.11", "HeuristicLab.ExtLibs\HeuristicLab.SimSharp\3.0.11\HeuristicLab.SimSharp-3.0.11\HeuristicLab.SimSharp-3.0.11.csproj", "{5C93CAF7-AE6A-4540-95B2-5DA6761A2677}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.MathJax-2.6.1", "HeuristicLab.ExtLibs\HeuristicLab.MathJax\2.6.1\HeuristicLab.MathJax-2.6.1\HeuristicLab.MathJax-2.6.1.csproj", "{BCDA17A6-5B4A-495C-B182-E856CA081BE6}"
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/HeuristicLab.SimSharp-3.0.11.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/HeuristicLab.SimSharp-3.0.11.csproj	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/HeuristicLab.SimSharp-3.0.11.csproj	(revision 16240)
@@ -0,0 +1,128 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{5C93CAF7-AE6A-4540-95B2-5DA6761A2677}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.SimSharp</RootNamespace>
+    <AssemblyName>HeuristicLab.SimSharp-3.0.11</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SignAssembly>true</SignAssembly>
+    <AssemblyOriginatorKeyFile>HeuristicLab.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Plugin.cs.frame" />
+    <Compile Include="Plugin.cs" />
+    <None Include="Properties\AssemblyInfo.cs.frame" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="HeuristicLab.snk" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\HeuristicLab.PluginInfrastructure\3.3\HeuristicLab.PluginInfrastructure-3.3.csproj">
+      <Project>{94186a6a-5176-4402-ae83-886557b53cca}</Project>
+      <Name>HeuristicLab.PluginInfrastructure-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\SimSharp-3.0.11\SimSharp-3.0.11.csproj">
+      <Project>{9fe9c740-7859-4d01-ad07-7d4e15a6320b}</Project>
+      <Name>SimSharp-3.0.11</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="SimSharp-3.0.11 License.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">
+      set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+      set ProjectDir=$(ProjectDir)
+      set SolutionDir=$(SolutionDir)
+      set Outdir=$(Outdir)
+
+      call PreBuildEvent.cmd
+    </PreBuildEvent>
+    <PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
+      export ProjectDir=$(ProjectDir)
+      export SolutionDir=$(SolutionDir)
+
+      $SolutionDir/PreBuildEvent.sh
+    </PreBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/Plugin.cs.frame
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/Plugin.cs.frame	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/Plugin.cs.frame	(revision 16240)
@@ -0,0 +1,31 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.SimSharp {
+  [Plugin("HeuristicLab.SimSharp", "Transport plugin for the simulation framework Sim#", "3.0.11.$WCREV$")]
+  [PluginFile("HeuristicLab.SimSharp-3.0.11.dll", PluginFileType.Assembly)]
+  [PluginFile("SimSharp-3.0.11.dll", PluginFileType.Assembly)]
+  [PluginFile("SimSharp-3.0.11 License.txt", PluginFileType.License)]
+  public class HeuristicLabSimSharpPlugin : PluginBase {
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/Properties/AssemblyInfo.cs.frame
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/Properties/AssemblyInfo.cs.frame	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/Properties/AssemblyInfo.cs.frame	(revision 16240)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeuristicLab.SimSharp-3.0.11")]
+[assembly: AssemblyDescription("Transport plugin for the simulation framework Sim#")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HEAL")]
+[assembly: AssemblyProduct("HeuristicLab")]
+[assembly: AssemblyCopyright("(c) 2002-2018 HEAL")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("15bc80f5-e0b9-4481-bab1-289d1403f877")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("3.0.11.0")]
+[assembly: AssemblyFileVersion("3.0.11.$WCREV$")]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/SimSharp-3.0.11 License.txt
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/SimSharp-3.0.11 License.txt	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/HeuristicLab.SimSharp-3.0.11/SimSharp-3.0.11 License.txt	(revision 16240)
@@ -0,0 +1,705 @@
+Sim# integrates code from a third-party "BlueRaja". Below the license from that
+third party provider as well as Sim#'s own license are given.
+Sim# also uses FastRandom from Colin Green (sharpneat@gmail.com) which is also
+licensed under the GPL. 
+
+=== License of BlueRaja's Event Queue ===
+
+The MIT License (MIT)
+
+Copyright (c) 2013 Daniel "BlueRaja" Pflughoeft
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+=== License of SimSharp and FastRandom ===
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Analysis/ContinuousStatistics.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Analysis/ContinuousStatistics.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Analysis/ContinuousStatistics.cs	(revision 16240)
@@ -0,0 +1,72 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public sealed class ContinuousStatistics {
+    private readonly Environment env;
+
+    public int Count { get; private set; }
+    public double TotalTimeD { get; private set; }
+    public TimeSpan TotalTime { get { return env.ToTimeSpan(TotalTimeD); } }
+
+    public double Min { get; private set; }
+    public double Max { get; private set; }
+    public double Area { get; private set; }
+    public double Mean { get; private set; }
+    public double StdDev { get { return Math.Sqrt(Variance); } }
+    public double Variance { get { return (TotalTimeD > 0) ? variance / TotalTimeD : 0.0; } }
+
+    private double lastUpdateTime;
+    private double lastValue;
+    private double variance;
+
+    private bool firstSample;
+
+
+    public ContinuousStatistics(Environment env) {
+      this.env = env;
+      lastUpdateTime = env.NowD;
+    }
+
+    public void Update(double value) {
+      Count++;
+
+      if (!firstSample) {
+        Min = Max = Mean = value;
+        firstSample = true;
+      } else {
+        if (value < Min) Min = value;
+        if (value > Max) Max = value;
+
+        var duration = env.NowD - lastUpdateTime;
+        if (duration > 0) {
+          Area += (lastValue * duration);
+          var oldMean = Mean;
+          Mean = oldMean + (lastValue - oldMean) * duration / (duration + TotalTimeD);
+          variance = variance + (lastValue - oldMean) * (lastValue - Mean) * duration;
+          TotalTimeD += duration;
+        }
+      }
+
+      lastUpdateTime = env.NowD;
+      lastValue = value;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueue-License.txt
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueue-License.txt	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueue-License.txt	(revision 16240)
@@ -0,0 +1,21 @@
+﻿The MIT License (MIT)
+
+Copyright (c) 2013 Daniel "BlueRaja" Pflughoeft
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueue.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueue.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueue.cs	(revision 16240)
@@ -0,0 +1,287 @@
+﻿using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace SimSharp {
+  /// <summary>
+  /// An implementation of a min-Priority Queue using a heap.  Has O(1) .Contains()!
+  /// See https://bitbucket.org/BlueRaja/high-speed-priority-queue-for-c/wiki/Getting%20Started for more information
+  /// </summary>
+  /// <remarks>
+  /// There are modifications so that the type is not generic anymore and can only hold values of type EventQueueNode
+  /// </remarks>
+  public sealed class EventQueue : IEnumerable<EventQueueNode> {
+    private int _numNodes;
+    private readonly EventQueueNode[] _nodes;
+    private long _numNodesEverEnqueued;
+
+    /// <summary>
+    /// Instantiate a new Priority Queue
+    /// </summary>
+    /// <param name="maxNodes">EventQueueNodehe max nodes ever allowed to be enqueued (going over this will cause an exception)</param>
+    public EventQueue(int maxNodes) {
+      _numNodes = 0;
+      _nodes = new EventQueueNode[maxNodes + 1];
+      _numNodesEverEnqueued = 0;
+    }
+
+    /// <summary>
+    /// Returns the number of nodes in the queue.  O(1)
+    /// </summary>
+    public int Count {
+      get {
+        return _numNodes;
+      }
+    }
+
+    /// <summary>
+    /// Returns the maximum number of items that can be enqueued at once in this queue.  Once you hit this number (ie. once Count == MaxSize),
+    /// attempting to enqueue another item will throw an exception.  O(1)
+    /// </summary>
+    public int MaxSize {
+      get {
+        return _nodes.Length - 1;
+      }
+    }
+
+    /// <summary>
+    /// Removes every node from the queue.  O(n) (So, don't do this often!)
+    /// </summary>
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    public void Clear() {
+      Array.Clear(_nodes, 1, _numNodes);
+      _numNodes = 0;
+    }
+
+    /// <summary>
+    /// Returns (in O(1)!) whether the given node is in the queue.  O(1)
+    /// </summary>
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    public bool Contains(EventQueueNode node) {
+      return (_nodes[node.QueueIndex] == node);
+    }
+
+    /// <summary>
+    /// Enqueue a node - .Priority must be set beforehand!  O(log n)
+    /// </summary>
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    public EventQueueNode Enqueue(DateTime primaryPriority, Event @event, int secondaryPriority = 0) {
+      var node = new EventQueueNode {
+        PrimaryPriority = primaryPriority,
+        SecondaryPriority = secondaryPriority,
+        Event = @event,
+        QueueIndex = ++_numNodes,
+        InsertionIndex = _numNodesEverEnqueued++
+      };
+      _nodes[_numNodes] = node;
+      CascadeUp(_nodes[_numNodes]);
+      return node;
+    }
+
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    private void Swap(EventQueueNode node1, EventQueueNode node2) {
+      //Swap the nodes
+      _nodes[node1.QueueIndex] = node2;
+      _nodes[node2.QueueIndex] = node1;
+
+      //Swap their indicies
+      int temp = node1.QueueIndex;
+      node1.QueueIndex = node2.QueueIndex;
+      node2.QueueIndex = temp;
+    }
+
+    //Performance appears to be slightly better when this is NOT inlined o_O
+    private void CascadeUp(EventQueueNode node) {
+      //aka Heapify-up
+      int parent = node.QueueIndex / 2;
+      while (parent >= 1) {
+        EventQueueNode parentNode = _nodes[parent];
+        if (HasHigherPriority(parentNode, node))
+          break;
+
+        //Node has lower priority value, so move it up the heap
+        Swap(node, parentNode); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown()
+
+        parent = node.QueueIndex / 2;
+      }
+    }
+
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    private void CascadeDown(EventQueueNode node) {
+      //aka Heapify-down
+      EventQueueNode newParent;
+      int finalQueueIndex = node.QueueIndex;
+      while (true) {
+        newParent = node;
+        int childLeftIndex = 2 * finalQueueIndex;
+
+        //Check if the left-child is higher-priority than the current node
+        if (childLeftIndex > _numNodes) {
+          //This could be placed outside the loop, but then we'd have to check newParent != node twice
+          node.QueueIndex = finalQueueIndex;
+          _nodes[finalQueueIndex] = node;
+          break;
+        }
+
+        EventQueueNode childLeft = _nodes[childLeftIndex];
+        if (HasHigherPriority(childLeft, newParent)) {
+          newParent = childLeft;
+        }
+
+        //Check if the right-child is higher-priority than either the current node or the left child
+        int childRightIndex = childLeftIndex + 1;
+        if (childRightIndex <= _numNodes) {
+          EventQueueNode childRight = _nodes[childRightIndex];
+          if (HasHigherPriority(childRight, newParent)) {
+            newParent = childRight;
+          }
+        }
+
+        //If either of the children has higher (smaller) priority, swap and continue cascading
+        if (newParent != node) {
+          //Move new parent to its new index.  node will be moved once, at the end
+          //Doing it this way is one less assignment operation than calling Swap()
+          _nodes[finalQueueIndex] = newParent;
+
+          int temp = newParent.QueueIndex;
+          newParent.QueueIndex = finalQueueIndex;
+          finalQueueIndex = temp;
+        } else {
+          //See note above
+          node.QueueIndex = finalQueueIndex;
+          _nodes[finalQueueIndex] = node;
+          break;
+        }
+      }
+    }
+
+    /// <summary>
+    /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+    /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+    /// </summary>
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    private bool HasHigherPriority(EventQueueNode higher, EventQueueNode lower) {
+      return (higher.PrimaryPriority < lower.PrimaryPriority ||
+          (higher.PrimaryPriority == lower.PrimaryPriority
+            && (higher.SecondaryPriority < lower.SecondaryPriority ||
+            (higher.SecondaryPriority == lower.SecondaryPriority
+              && higher.InsertionIndex < lower.InsertionIndex))));
+    }
+
+    /// <summary>
+    /// Removes the head of the queue (node with highest priority; ties are broken by order of insertion), and returns it.  O(log n)
+    /// </summary>
+    public EventQueueNode Dequeue() {
+      EventQueueNode returnMe = _nodes[1];
+      Remove(returnMe);
+      return returnMe;
+    }
+
+    /// <summary>
+    /// Returns the head of the queue, without removing it (use Dequeue() for that).  O(1)
+    /// </summary>
+    public EventQueueNode First {
+      get {
+        return _nodes[1];
+      }
+    }
+
+    /// <summary>
+    /// This method must be called on a node every time its priority changes while it is in the queue.  
+    /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+    /// O(log n)
+    /// </summary>
+#if AGGRESSIVE_INLINING
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#endif
+    public void UpdatePriority(EventQueueNode node, DateTime primaryPriority, int secondaryPriority) {
+      node.PrimaryPriority = primaryPriority;
+      node.SecondaryPriority = secondaryPriority;
+      OnNodeUpdated(node);
+    }
+
+    internal void OnNodeUpdated(EventQueueNode node) {
+      //Bubble the updated node up or down as appropriate
+      int parentIndex = node.QueueIndex / 2;
+      EventQueueNode parentNode = _nodes[parentIndex];
+
+      if (parentIndex > 0 && HasHigherPriority(node, parentNode)) {
+        CascadeUp(node);
+      } else {
+        //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+        CascadeDown(node);
+      }
+    }
+
+    /// <summary>
+    /// Removes a node from the queue.  Note that the node does not need to be the head of the queue.  O(log n)
+    /// </summary>
+    public void Remove(EventQueueNode node) {
+      if (!Contains(node)) {
+        return;
+      }
+      if (_numNodes <= 1) {
+        _nodes[1] = null;
+        _numNodes = 0;
+        return;
+      }
+
+      //Make sure the node is the last node in the queue
+      bool wasSwapped = false;
+      EventQueueNode formerLastNode = _nodes[_numNodes];
+      if (node.QueueIndex != _numNodes) {
+        //Swap the node with the last node
+        Swap(node, formerLastNode);
+        wasSwapped = true;
+      }
+
+      _numNodes--;
+      _nodes[node.QueueIndex] = null;
+
+      if (wasSwapped) {
+        //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+        OnNodeUpdated(formerLastNode);
+      }
+    }
+
+    public IEnumerator<EventQueueNode> GetEnumerator() {
+      for (int i = 1; i <= _numNodes; i++)
+        yield return _nodes[i];
+    }
+
+    IEnumerator IEnumerable.GetEnumerator() {
+      return GetEnumerator();
+    }
+
+    /// <summary>
+    /// <b>Should not be called in production code.</b>
+    /// Checks to make sure the queue is still in a valid state.  Used for testing/debugging the queue.
+    /// </summary>
+    public bool IsValidQueue() {
+      for (int i = 1; i < _nodes.Length; i++) {
+        if (_nodes[i] != null) {
+          int childLeftIndex = 2 * i;
+          if (childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+            return false;
+
+          int childRightIndex = childLeftIndex + 1;
+          if (childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+            return false;
+        }
+      }
+      return true;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueueNode.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueueNode.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Collections/EventQueueNode.cs	(revision 16240)
@@ -0,0 +1,40 @@
+﻿using System;
+
+namespace SimSharp {
+  public class EventQueueNode {
+    /// <summary>
+    /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue.
+    /// </summary>
+    public DateTime PrimaryPriority { get; set; }
+
+    /// <summary>
+    /// An integer priority that will be used to distinguish nodes with the same
+    /// <see cref="PrimaryPriority"/> and before the <see cref="InsertionIndex"/> is used.
+    /// Must be set BEFORE adding a node to the queue.
+    /// </summary>
+    /// <remarks>
+    /// A lower value means higher priority, thus int.MinValue has highest priority.
+    /// </remarks>
+    public int SecondaryPriority { get; set; }
+
+    /// <summary>
+    /// The event that is associated with this node.
+    /// </summary>
+    public Event Event { get; set; }
+
+    /// <summary>
+    /// <b>Used by the priority queue - do not edit this value.</b>
+    /// Represents the order the node was inserted in
+    /// </summary>
+    /// <remarks>
+    /// This is unique among all inserted nodes and thus represents the third and final priority.
+    /// </remarks>
+    public long InsertionIndex { get; set; }
+
+    /// <summary>
+    /// <b>Used by the priority queue - do not edit this value.</b>
+    /// Represents the current position in the queue
+    /// </summary>
+    public int QueueIndex { get; set; }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/ActiveObject.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/ActiveObject.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/ActiveObject.cs	(revision 16240)
@@ -0,0 +1,27 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+namespace SimSharp {
+  public abstract class ActiveObject<T> where T : Environment {
+    protected T Environment { get; private set; }
+
+    protected ActiveObject(T environment) {
+      Environment = environment;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Environment.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Environment.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Environment.cs	(revision 16240)
@@ -0,0 +1,420 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace SimSharp {
+  /// <summary>
+  /// Environments hold the event queues, schedule and process events.
+  /// </summary>
+  public class Environment {
+    private const int InitialMaxEvents = 1024;
+    private object locker = new object();
+
+    /// <summary>
+    /// Describes the number of seconds that a logical step of 1 in the *D-API takes.
+    /// </summary>
+    protected double DefaultTimeStepSeconds { get; private set; }
+
+    /// <summary>
+    /// Calculates the logical date of the simulation by the amount of default steps
+    /// that have passed.
+    /// </summary>
+    public double NowD {
+      get { return (Now - StartDate).TotalSeconds / DefaultTimeStepSeconds; }
+    }
+
+    /// <summary>
+    /// The current simulation time as a calendar date.
+    /// </summary>
+    public DateTime Now { get; protected set; }
+
+    /// <summary>
+    /// The calendar date when the simulation started. This defaults to 1970-1-1 if
+    /// no other date has been specified in the overloaded constructor.
+    /// </summary>
+    public DateTime StartDate { get; protected set; }
+
+    /// <summary>
+    /// The random number generator that is to be used in all events in
+    /// order to produce reproducible results.
+    /// </summary>
+    protected IRandom Random { get; set; }
+
+    protected EventQueue ScheduleQ;
+    public Process ActiveProcess { get; set; }
+
+    public TextWriter Logger { get; set; }
+    public int ProcessedEvents { get; protected set; }
+
+    public Environment() : this(new DateTime(1970, 1, 1)) { }
+    public Environment(TimeSpan? defaultStep) : this(new DateTime(1970, 1, 1), defaultStep) { }
+    public Environment(int randomSeed, TimeSpan? defaultStep = null) : this(new DateTime(1970, 1, 1), randomSeed, defaultStep) { }
+    public Environment(DateTime initialDateTime, TimeSpan? defaultStep = null) {
+      DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds;
+      StartDate = initialDateTime;
+      Now = initialDateTime;
+      Random = new SystemRandom();
+      ScheduleQ = new EventQueue(InitialMaxEvents);
+      Logger = Console.Out;
+    }
+    public Environment(DateTime initialDateTime, int randomSeed, TimeSpan? defaultStep = null) {
+      DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds;
+      StartDate = initialDateTime;
+      Now = initialDateTime;
+      Random = new SystemRandom(randomSeed);
+      ScheduleQ = new EventQueue(InitialMaxEvents);
+      Logger = Console.Out;
+    }
+
+    public double ToDouble(TimeSpan span) {
+      return span.TotalSeconds / DefaultTimeStepSeconds;
+    }
+
+    public TimeSpan ToTimeSpan(double span) {
+      return TimeSpan.FromSeconds(DefaultTimeStepSeconds * span);
+    }
+
+    /// <summary>
+    /// Creates a new process from an event generator. The process is automatically
+    /// scheduled to be started at the current simulation time.
+    /// </summary>
+    /// <param name="generator">The generator function that represents the process.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    /// <returns>The scheduled process that was created.</returns>
+    public Process Process(IEnumerable<Event> generator, int priority = 0) {
+      return new Process(this, generator, priority);
+    }
+
+    /// <summary>
+    /// Creates and returns a new timeout.
+    /// </summary>
+    /// <param name="delay">The time after which the timeout is fired.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    /// <returns>The scheduled timeout event that was created.</returns>
+    public Timeout TimeoutD(double delay, int priority = 0) {
+      return Timeout(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay), priority);
+    }
+
+    /// <summary>
+    /// Creates and returns a new timeout.
+    /// </summary>
+    /// <param name="delay">The time after which the timeout is fired.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    /// <returns>The scheduled timeout event that was created.</returns>
+    public Timeout Timeout(TimeSpan delay, int priority = 0) {
+      return new Timeout(this, delay, priority: priority);
+    }
+
+    public virtual void Reset(int randomSeed) {
+      ProcessedEvents = 0;
+      Now = StartDate;
+      Random = new SystemRandom(randomSeed);
+      ScheduleQ = new EventQueue(InitialMaxEvents);
+    }
+
+    public virtual void ScheduleD(double delay, Event @event) {
+      Schedule(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay), @event);
+    }
+
+    /// <summary>
+    /// Schedules an event to occur at the same simulation time as the call was made.
+    /// </summary>
+    /// <param name="event">The event that should be scheduled.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param></param>
+    public virtual void Schedule(Event @event, int priority = 0) {
+      lock (locker) {
+        DoSchedule(Now, @event, priority);
+      }
+    }
+
+    /// <summary>
+    /// Schedules an event to occur after a certain (positive) delay.
+    /// </summary>
+    /// <exception cref="ArgumentException">
+    /// Thrown when <paramref name="delay"/> is negative.
+    /// </exception>
+    /// <param name="delay">The (positive) delay after which the event should be fired.</param>
+    /// <param name="event">The event that should be scheduled.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    public virtual void Schedule(TimeSpan delay, Event @event, int priority = 0) {
+      if (delay < TimeSpan.Zero)
+        throw new ArgumentException("Negative delays are not allowed in Schedule(TimeSpan, Event).");
+      lock (locker) {
+        var eventTime = Now + delay;
+        DoSchedule(eventTime, @event, priority);
+      }
+    }
+
+    protected virtual EventQueueNode DoSchedule(DateTime date, Event @event, int priority = 0) {
+      if (ScheduleQ.MaxSize == ScheduleQ.Count) {
+        // the capacity has to be adjusted, there are more events in the queue than anticipated
+        var oldSchedule = ScheduleQ;
+        ScheduleQ = new EventQueue(ScheduleQ.MaxSize * 2);
+        foreach (var e in oldSchedule) ScheduleQ.Enqueue(e.PrimaryPriority, e.Event, e.SecondaryPriority);
+      }
+      return ScheduleQ.Enqueue(date, @event, priority);
+    }
+
+    public virtual object RunD(double? until = null) {
+      if (!until.HasValue) return Run();
+      return Run(Now + TimeSpan.FromSeconds(DefaultTimeStepSeconds * until.Value));
+    }
+
+    public virtual object Run(TimeSpan span) {
+      return Run(Now + span);
+    }
+
+    public virtual object Run(DateTime until) {
+      if (until <= Now) throw new InvalidOperationException("Simulation end date must lie in the future.");
+      var stopEvent = new Event(this);
+      var node = DoSchedule(until, stopEvent);
+      // stop event is always the first to execute at the given time
+      node.InsertionIndex = -1;
+      ScheduleQ.OnNodeUpdated(node);
+      return Run(stopEvent);
+    }
+
+    public virtual object Run(Event stopEvent = null) {
+      if (stopEvent != null) {
+        if (stopEvent.IsProcessed) return stopEvent.Value;
+        stopEvent.AddCallback(StopSimulation);
+      }
+
+      try {
+        var stop = ScheduleQ.Count == 0;
+        while (!stop) {
+          Step();
+          ProcessedEvents++;
+          lock (locker) {
+            stop = ScheduleQ.Count == 0;
+          }
+        }
+      } catch (StopSimulationException e) { return e.Value; }
+      if (stopEvent == null) return null;
+      if (!stopEvent.IsTriggered) throw new InvalidOperationException("No scheduled events left but \"until\" event was not triggered.");
+      return stopEvent.Value;
+    }
+
+    public virtual void Step() {
+      Event evt;
+      lock (locker) {
+        var next = ScheduleQ.Dequeue();
+        Now = next.PrimaryPriority;
+        evt = next.Event;
+      }
+      evt.Process();
+    }
+
+    public virtual double PeekD() {
+      lock (locker) {
+        if (ScheduleQ.Count == 0) return double.MaxValue;
+        return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds;
+      }
+    }
+
+    public virtual DateTime Peek() {
+      lock (locker) {
+        return ScheduleQ.Count > 0 ? ScheduleQ.First.PrimaryPriority : DateTime.MaxValue;
+      }
+    }
+
+    protected virtual void StopSimulation(Event @event) {
+      throw new StopSimulationException(@event.Value);
+    }
+
+    public virtual void Log(string message, params object[] args) {
+      if (Logger != null)
+        Logger.WriteLine(message, args);
+    }
+
+    #region Random number distributions
+    protected static readonly double NormalMagicConst = 4 * Math.Exp(-0.5) / Math.Sqrt(2.0);
+
+    public double RandUniform(double a, double b) {
+      return a + (b - a) * Random.NextDouble();
+    }
+
+    public TimeSpan RandUniform(TimeSpan a, TimeSpan b) {
+      return TimeSpan.FromSeconds(RandUniform(a.TotalSeconds, b.TotalSeconds));
+    }
+
+    public double RandTriangular(double low, double high) {
+      var u = Random.NextDouble();
+      if (u > 0.5)
+        return high + (low - high) * Math.Sqrt(((1.0 - u) / 2));
+      return low + (high - low) * Math.Sqrt(u / 2);
+    }
+
+    public TimeSpan RandTriangular(TimeSpan low, TimeSpan high) {
+      return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds));
+    }
+
+    public double RandTriangular(double low, double high, double mode) {
+      var u = Random.NextDouble();
+      var c = (mode - low) / (high - low);
+      if (u > c)
+        return high + (low - high) * Math.Sqrt(((1.0 - u) * (1.0 - c)));
+      return low + (high - low) * Math.Sqrt(u * c);
+    }
+
+    public TimeSpan RandTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {
+      return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds, mode.TotalSeconds));
+    }
+
+    /// <summary>
+    /// Returns a number that is exponentially distributed given a certain mean.
+    /// </summary>
+    /// <remarks>
+    /// Unlike in other APIs here the mean should be given and not the lambda parameter.
+    /// </remarks>
+    /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param>
+    /// <returns>A number that is exponentially distributed</returns>
+    public double RandExponential(double mean) {
+      return -Math.Log(1 - Random.NextDouble()) * mean;
+    }
+
+    /// <summary>
+    /// Returns a timespan that is exponentially distributed given a certain mean.
+    /// </summary>
+    /// <remarks>
+    /// Unlike in other APIs here the mean should be given and not the lambda parameter.
+    /// </remarks>
+    /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param>
+    /// <returns>A number that is exponentially distributed</returns>
+    public TimeSpan RandExponential(TimeSpan mean) {
+      return TimeSpan.FromSeconds(RandExponential(mean.TotalSeconds));
+    }
+
+    public double RandNormal(double mu, double sigma) {
+      double z, zz, u1, u2;
+      do {
+        u1 = Random.NextDouble();
+        u2 = 1 - Random.NextDouble();
+        z = NormalMagicConst * (u1 - 0.5) / u2;
+        zz = z * z / 4.0;
+      } while (zz > -Math.Log(u2));
+      return mu + z * sigma;
+    }
+
+    public TimeSpan RandNormal(TimeSpan mu, TimeSpan sigma) {
+      return TimeSpan.FromSeconds(RandNormal(mu.TotalSeconds, sigma.TotalSeconds));
+    }
+
+    public double RandNormalPositive(double mu, double sigma) {
+      double val;
+      do {
+        val = RandNormal(mu, sigma);
+      } while (val <= 0);
+      return val;
+    }
+
+    public TimeSpan RandNormalPositive(TimeSpan mu, TimeSpan sigma) {
+      return TimeSpan.FromSeconds(RandNormalPositive(mu.TotalSeconds, sigma.TotalSeconds));
+    }
+
+    public double RandNormalNegative(double mu, double sigma) {
+      double val;
+      do {
+        val = RandNormal(mu, sigma);
+      } while (val >= 0);
+      return val;
+    }
+
+    public TimeSpan RandNormalNegative(TimeSpan mu, TimeSpan sigma) {
+      return TimeSpan.FromSeconds(RandNormalNegative(mu.TotalSeconds, sigma.TotalSeconds));
+    }
+
+    public double RandLogNormal(double mu, double sigma) {
+      return Math.Exp(RandNormal(mu, sigma));
+    }
+
+    public TimeSpan RandLogNormal(TimeSpan mu, TimeSpan sigma) {
+      return TimeSpan.FromSeconds(RandLogNormal(mu.TotalSeconds, sigma.TotalSeconds));
+    }
+
+    public double RandCauchy(double x0, double gamma) {
+      return x0 + gamma * Math.Tan(Math.PI * (Random.NextDouble() - 0.5));
+    }
+
+    public TimeSpan RandCauchy(TimeSpan x0, TimeSpan gamma) {
+      return TimeSpan.FromSeconds(RandCauchy(x0.TotalSeconds, gamma.TotalSeconds));
+    }
+
+    public double RandWeibull(double alpha, double beta) {
+      return alpha * Math.Pow(-Math.Log(1 - Random.NextDouble()), 1 / beta);
+    }
+
+    public TimeSpan RandWeibull(TimeSpan mu, TimeSpan sigma) {
+      return TimeSpan.FromSeconds(RandWeibull(mu.TotalSeconds, sigma.TotalSeconds));
+    }
+    #endregion
+
+    #region Random timeouts
+    public Timeout TimeoutUniformD(double a, double b) {
+      return new Timeout(this, ToTimeSpan(RandUniform(a, b)));
+    }
+
+    public Timeout TimeoutUniform(TimeSpan a, TimeSpan b) {
+      return new Timeout(this, RandUniform(a, b));
+    }
+
+    public Timeout TimeoutTriangularD(double low, double high) {
+      return new Timeout(this, ToTimeSpan(RandTriangular(low, high)));
+    }
+
+    public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high) {
+      return new Timeout(this, RandTriangular(low, high));
+    }
+
+    public Timeout TimeoutTriangularD(double low, double high, double mode) {
+      return new Timeout(this, ToTimeSpan(RandTriangular(low, high, mode)));
+    }
+
+    public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {
+      return new Timeout(this, RandTriangular(low, high, mode));
+    }
+
+    public Timeout TimeoutExponentialD(double mean) {
+      return new Timeout(this, ToTimeSpan(RandExponential(mean)));
+    }
+
+    public Timeout TimeoutExponential(TimeSpan mean) {
+      return new Timeout(this, RandExponential(mean));
+    }
+
+    public Timeout TimeoutNormalPositiveD(double mu, double sigma) {
+      return new Timeout(this, ToTimeSpan(RandNormalPositive(mu, sigma)));
+    }
+
+    public Timeout TimeoutNormalPositive(TimeSpan mu, TimeSpan sigma) {
+      return new Timeout(this, RandNormalPositive(mu, sigma));
+    }
+
+    public Timeout TimeoutLogNormalD(double mu, double sigma) {
+      return new Timeout(this, ToTimeSpan(RandLogNormal(mu, sigma)));
+    }
+
+    public Timeout TimeoutLogNormal(TimeSpan mu, TimeSpan sigma) {
+      return new Timeout(this, RandLogNormal(mu, sigma));
+    }
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/AllOf.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/AllOf.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/AllOf.cs	(revision 16240)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System.Collections.Generic;
+
+namespace SimSharp {
+  public class AllOf : Condition {
+    public AllOf(Environment environment, params Event[] events) : base(environment, events) { }
+    public AllOf(Environment environment, IEnumerable<Event> events) : base(environment, events) { }
+
+    protected override bool Evaluate() {
+      return FiredEvents.Count == Events.Count;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/AnyOf.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/AnyOf.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/AnyOf.cs	(revision 16240)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System.Collections.Generic;
+
+namespace SimSharp {
+  public class AnyOf : Condition {
+    public AnyOf(Environment environment, params Event[] events) : base(environment, events) { }
+    public AnyOf(Environment environment, IEnumerable<Event> events) : base(environment, events) { }
+
+    protected override bool Evaluate() {
+      return FiredEvents.Count > 0 || Events.Count == 0;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Condition.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Condition.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Condition.cs	(revision 16240)
@@ -0,0 +1,97 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+
+namespace SimSharp {
+  /// <summary>
+  /// Conditions are events that execute when any or all of its sub-events are executed.
+  /// </summary>
+  public abstract class Condition : Event {
+
+    public new OrderedDictionary Value {
+      get { return (OrderedDictionary)base.Value; }
+      set { base.Value = value; }
+    }
+
+    protected List<Event> Events { get; private set; }
+
+    protected List<Event> FiredEvents { get; private set; }
+
+    protected Condition(Environment environment, params Event[] events)
+      : this(environment, (IEnumerable<Event>)events) { }
+    protected Condition(Environment environment, IEnumerable<Event> events)
+      : base(environment) {
+      CallbackList.Add(CollectValues);
+      Events = new List<Event>(events);
+      FiredEvents = new List<Event>();
+
+      foreach (var @event in Events) {
+        if (Environment != @event.Environment)
+          throw new ArgumentException("It is not allowed to mix events from different environments");
+        if (@event.IsProcessed) Check(@event);
+        else @event.AddCallback(Check);
+      }
+
+      if (IsAlive && Evaluate())
+        Succeed();
+    }
+
+    protected void Check(Event @event) {
+      if (IsTriggered || IsProcessed) {
+        if (!@event.IsOk) throw new InvalidOperationException(
+@"Errors that happen after the condition has been triggered will not be
+handled by the condition and cause the simulation to crash.");
+        return;
+      }
+      FiredEvents.Add(@event);
+
+      if (!@event.IsOk)
+        Fail(@event.Value);
+      else if (Evaluate()) {
+        Succeed();
+      }
+    }
+
+    protected virtual IEnumerable<KeyValuePair<object, object>> GetValues() {
+      var values = new List<KeyValuePair<object, object>>();
+      foreach (var e in Events) {
+        var condition = e as Condition;
+        if (condition != null) {
+          values.AddRange(condition.GetValues());
+        } else if (e.IsProcessed) {
+          values.Add(new KeyValuePair<object, object>(e, e.Value));
+        }
+      }
+      return values;
+    }
+
+    protected virtual void CollectValues(Event @event) {
+      if (@event.IsOk) {
+        var value = new OrderedDictionary();
+        foreach (var v in GetValues())
+          value.Add(v.Key, v.Value);
+        Value = value;
+      }
+    }
+
+    protected abstract bool Evaluate();
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Event.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Event.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Event.cs	(revision 16240)
@@ -0,0 +1,200 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace SimSharp {
+  /// <summary>
+  /// The base class for all events in SimSharp.
+  /// An event can be in one of three states at any time:
+  ///  - Alive: The event object exists, but is neither scheduled to
+  ///           be executed, nor is it already executed.
+  ///  - Triggered: The event has been put in the event queue and is
+  ///               going to be executed.
+  ///  - Processed: The event has been executed.
+  /// 
+  /// Usually, the event is alive until its Trigger, Succeed, or Fail
+  /// method have been called. Then it becomes triggered. When the
+  /// Environment progresses to the event and executes its callbacks
+  /// the event becomes processed.
+  /// </summary>
+  public class Event {
+    protected internal Environment Environment { get; private set; }
+    protected List<Action<Event>> CallbackList { get; set; }
+
+    /// <summary>
+    /// The value property can be used to return arbitrary data from a
+    /// process or an event. It also represents the interrupt cause to
+    /// a process.
+    /// </summary>
+    public object Value { get; protected set; }
+
+    /// <summary>
+    /// The IsOk flag indicates if the event succeeded or failed. An event
+    /// that failed indicates to a waiting process that the action could
+    /// not be performed and that the faulting situation must be handled.
+    /// Typically, interrupting a process sets the IsOk flag to false.
+    /// </summary>
+    public bool IsOk { get; protected set; }
+    /// <summary>
+    /// An event is alive when it is not triggered and not processed. That
+    /// is, when it exists in memory without being scheduled. Typically,
+    /// a Process is alive until its last event has been processed and the
+    /// process event itself is to be processed.
+    /// </summary>
+    public bool IsAlive { get { return !IsTriggered && !IsProcessed; } }
+    /// <summary>
+    /// An event becomes processed when its callbacks have been executed.
+    /// Events may only be processed once and an exception will be thrown
+    /// if they are to be processed multiple times.
+    /// </summary>
+    public bool IsProcessed { get; protected set; }
+    /// <summary>
+    /// An event becomes triggered when it is placed into the event queue.
+    /// That is, when its callbacks are going to be executed.
+    /// An even that is triggered may later not be failed or retriggered.
+    /// </summary>
+    public bool IsTriggered { get; protected set; }
+
+    public Event(Environment environment) {
+      Environment = environment;
+      CallbackList = new List<Action<Event>>();
+    }
+
+    /// <summary>
+    /// This method schedules the event right now. It takes the IsOk state
+    /// and uses the <see cref="Value"/> of the given <paramref name="@event"/>.
+    /// Thus if the given event fails, this event will also be triggered as
+    /// failing.
+    /// </summary>
+    /// <exception cref="InvalidOperationException">
+    /// Thrown when the event has already been triggered.
+    /// </exception>
+    /// <remarks>
+    /// The signature of this method allows it to be used as a callback.
+    /// </remarks>
+    /// <param name="event">The event that triggers this event.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    public virtual void Trigger(Event @event, int priority = 0) {
+      if (IsTriggered)
+        throw new InvalidOperationException("Event has already been triggered.");
+      IsOk = @event.IsOk;
+      Value = @event.Value;
+      IsTriggered = true;
+      Environment.Schedule(this, priority);
+    }
+
+    /// <summary>
+    /// This method schedules the event right now. It sets IsOk state to true
+    /// and optionally uses also the value. If urgent is given, the event may
+    /// be scheduled as urgent. Urgent events are placed in a separate event
+    /// queue. The callbacks of urgent events are executed before normal events.
+    /// </summary>
+    /// <exception cref="InvalidOperationException">
+    /// Thrown when the event has already been triggered.
+    /// </exception>
+    /// <param name="value">The value that the event should use.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    public virtual void Succeed(object value = null, int priority = 0) {
+      if (IsTriggered)
+        throw new InvalidOperationException("Event has already been triggered.");
+      IsOk = true;
+      Value = value;
+      IsTriggered = true;
+      Environment.Schedule(this, priority);
+    }
+
+    /// <summary>
+    /// This method schedules the event right now. It sets IsOk state to false
+    /// and optionally uses also the value. If urgent is given, the event may
+    /// be scheduled as urgent. Urgent events are placed in a separate event
+    /// queue. The callbacks of urgent events are executed before normal events.
+    /// </summary>
+    /// <exception cref="InvalidOperationException">
+    /// Thrown when the event has already been triggered.
+    /// </exception>
+    /// <param name="value">The value that the event should use.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    public virtual void Fail(object value = null, int priority = 0) {
+      if (IsTriggered)
+        throw new InvalidOperationException("Event has already been triggered.");
+      IsOk = false;
+      Value = value;
+      IsTriggered = true;
+      Environment.Schedule(this, priority);
+    }
+
+    /// <summary>
+    /// This method adds a callback to the list of callbacks. Callbacks will be
+    /// executed in the order they have been added.
+    /// </summary>
+    /// <param name="callback">The callback to execute when the event is being
+    /// processed.</param>
+    public virtual void AddCallback(Action<Event> callback) {
+      if (IsProcessed) throw new InvalidOperationException("Event is already processed.");
+      CallbackList.Add(callback);
+    }
+
+    /// <summary>
+    /// This method adds a range of callbacks to the list of callbacks. Callbacks
+    /// will be executed in the order they have been added.
+    /// </summary>
+    /// <param name="callbacks">The callbacks to execute when the event is being
+    /// processed.</param>
+    public virtual void AddCallbacks(IEnumerable<Action<Event>> callbacks) {
+      if (IsProcessed) throw new InvalidOperationException("Event is already processed.");
+      CallbackList.AddRange(callbacks);
+    }
+
+    /// <summary>
+    /// This method removes a callback to the list of callbacks.
+    /// </summary>
+    /// <remarks>
+    /// It is not checked if the callback has actually been added before and
+    /// no exception will be thrown if it had not been present.
+    /// </remarks>
+    /// <param name="callback">The callback to remove.</param>
+    public virtual void RemoveCallback(Action<Event> callback) {
+      if (IsProcessed) throw new InvalidOperationException("Event is already processed.");
+      CallbackList.Remove(callback);
+    }
+
+    /// <summary>
+    /// This method processes the event, that is, it calls all the callbacks.
+    /// When it finishes it will be marked IsProcessed and cannot be processed
+    /// again.
+    /// </summary>
+    /// <exception cref="InvalidOperationException">When the event has already
+    /// been processed.</exception>
+    public virtual void Process() {
+      if (IsProcessed) throw new InvalidOperationException("Event has already been processed.");
+      IsProcessed = true;
+      foreach (var callback in CallbackList)
+        callback(this);
+      CallbackList = null;
+    }
+
+    public static Condition operator &(Event event1, Event event2) {
+      return new AllOf(event1.Environment, event1, event2);
+    }
+    public static Condition operator |(Event event1, Event event2) {
+      return new AnyOf(event1.Environment, event1, event2);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Process.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Process.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Process.cs	(revision 16240)
@@ -0,0 +1,167 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace SimSharp {
+  /// <summary>
+  /// A Process handles the iteration of events. Processes may define steps that
+  /// a certain entity in the simulation has to perform. Each time the process
+  /// should wait it yields an event and will be resumed when that event is processed.
+  /// </summary>
+  /// <remarks>
+  /// Since an iterator method does not have access to its process, the method can
+  /// retrieve the associated Process through the ActiveProcess property of the
+  /// environment. Each Process sets and resets that property during Resume.
+  /// </remarks>
+  public class Process : Event {
+    private readonly IEnumerator<Event> generator;
+    private Event target;
+    /// <summary>
+    /// Target is the event that is expected to be executed next in the process.
+    /// </summary>
+    public Event Target {
+      get { return target; }
+      protected set { target = value; }
+    }
+
+    /// <summary>
+    /// Sets up a new process.
+    /// The process places an initialize event into the event queue which starts
+    /// the process by retrieving events from the generator.
+    /// </summary>
+    /// <param name="environment">The environment in which the process lives.</param>
+    /// <param name="generator">The generator function of the process.</param>
+    /// <param name="priority">The priority if multiple processes are started at the same time.</param>
+    public Process(Environment environment, IEnumerable<Event> generator, int priority = 0)
+      : base(environment) {
+      this.generator = generator.GetEnumerator();
+      IsOk = true;
+      target = new Initialize(environment, this, priority);
+    }
+
+    /// <summary>
+    /// This interrupts a process and causes the IsOk flag to be set to false.
+    /// If a process is interrupted the iterator method needs to call HandleFault()
+    /// before continuing to yield further events.
+    /// </summary>
+    /// <exception cref="InvalidOperationException">This is thrown in three conditions:
+    ///  - If the process has already been triggered.
+    ///  - If the process attempts to interrupt itself.
+    ///  - If the process continues to yield events despite being faulted.</exception>
+    /// <param name="cause">The cause of the interrupt.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    public virtual void Interrupt(object cause = null, int priority = 0) {
+      if (IsTriggered) throw new InvalidOperationException("The process has terminated and cannot be interrupted.");
+      if (Environment.ActiveProcess == this) throw new InvalidOperationException("A process is not allowed to interrupt itself.");
+
+      var interruptEvent = new Event(Environment);
+      interruptEvent.AddCallback(Resume);
+      interruptEvent.Fail(cause, priority);
+
+      if (Target != null)
+        Target.RemoveCallback(Resume);
+    }
+
+    protected virtual void Resume(Event @event) {
+      Environment.ActiveProcess = this;
+      while (true) {
+        if (@event.IsOk) {
+          if (generator.MoveNext()) {
+            if (IsTriggered) {
+              // the generator called e.g. Environment.ActiveProcess.Fail
+              Environment.ActiveProcess = null;
+              return;
+            }
+            if (!ProceedToEvent()) {
+              @event = target;
+              continue;
+            } else break;
+          } else if (!IsTriggered) {
+            Succeed(@event.Value);
+            break;
+          } else break;
+        } else {
+          /* Fault handling differs from SimPy as in .NET it is not possible to inject an
+         * exception into an enumerator and it is impossible to put a yield return inside
+         * a try-catch block. In SimSharp the Process will set IsOk and will then move to
+         * the next yield in the generator. However, if after this move IsOk is still false
+         * we know that the error was not handled. It is assumed the error is handled if
+         * HandleFault() is called on the environment's ActiveProcess which will reset the
+         * flag. */
+          IsOk = false;
+          Value = @event.Value;
+
+          if (generator.MoveNext()) {
+            if (IsTriggered) {
+              // the generator called e.g. Environment.ActiveProcess.Fail
+              Environment.ActiveProcess = null;
+              return;
+            }
+            // if we move next, but IsOk is still false
+            if (!IsOk) throw new InvalidOperationException("The process did not react to being faulted.");
+            // otherwise HandleFault was called and the fault was handled
+            if (ProceedToEvent()) break;
+          } else if (!IsTriggered) {
+            if (!IsOk) Fail(@event.Value);
+            else Succeed(@event.Value);
+            break;
+          } else break;
+        }
+      }
+      Environment.ActiveProcess = null;
+    }
+
+    protected virtual bool ProceedToEvent() {
+      target = generator.Current;
+      Value = target.Value;
+      if (target.IsProcessed) return false;
+      target.AddCallback(Resume);
+      return true;
+    }
+
+    /// <summary>
+    /// This method must be called to reset the IsOk flag of the process back to true.
+    /// The IsOk flag may be set to false if the process waited on an event that failed.
+    /// </summary>
+    /// <remarks>
+    /// In SimPy a faulting process would throw an exception which is then catched and
+    /// chained. In SimSharp catching exceptions from a yield is not possible as a yield
+    /// return statement may not throw an exception.
+    /// If a processes faulted the Value property may indicate a cause for the fault.
+    /// </remarks>
+    /// <returns>True if a faulting situation needs to be handled, false if the process
+    /// is okay and the last yielded event succeeded.</returns>
+    public virtual bool HandleFault() {
+      if (IsOk) return false;
+      IsOk = true;
+      return true;
+    }
+
+    private class Initialize : Event {
+      public Initialize(Environment environment, Process process, int priority)
+        : base(environment) {
+        CallbackList.Add(process.Resume);
+        IsOk = true;
+        IsTriggered = true;
+        environment.Schedule(this, priority);
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Timeout.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Timeout.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Events/Timeout.cs	(revision 16240)
@@ -0,0 +1,46 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  /// <summary>
+  /// Timeouts are simple events that are executed after a certain timespan has passed.
+  /// </summary>
+  public sealed class Timeout : Event {
+    /// <summary>
+    /// A timeout is an event that is executed after a certain timespan has passed.
+    /// </summary>
+    /// <remarks>
+    /// Timeout events are scheduled when they are created. They are always triggered
+    /// when they are created.
+    /// </remarks>
+    /// <param name="environment">The environment in which it is scheduled.</param>
+    /// <param name="delay">The timespan for the timeout.</param>
+    /// <param name="value">The value of the timeout.</param>
+    /// <param name="isOk">Whether the timeout should succeed or fail.</param>
+    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
+    public Timeout(Environment environment, TimeSpan delay, object value = null, bool isOk = true, int priority = 0)
+      : base(environment) {
+      IsOk = isOk;
+      Value = value;
+      IsTriggered = true;
+      environment.Schedule(delay, this, priority);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Exceptions/EmptyScheduleException.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Exceptions/EmptyScheduleException.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Exceptions/EmptyScheduleException.cs	(revision 16240)
@@ -0,0 +1,32 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  /// <summary>
+  /// An exception that is thrown to stop the simulation.
+  /// </summary>
+  public class StopSimulationException : Exception {
+    public object Value { get; private set; }
+    
+    public StopSimulationException(object value) {
+      Value = value;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Preempted.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Preempted.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Preempted.cs	(revision 16240)
@@ -0,0 +1,31 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public sealed class Preempted {
+    public Process By { get; private set; }
+    public DateTime UsageSince { get; private set; }
+
+    public Preempted(Process by, DateTime usageSince) {
+      By = by;
+      UsageSince = usageSince;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Container.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Container.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Container.cs	(revision 16240)
@@ -0,0 +1,93 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimSharp {
+  public class Container {
+    public double Level { get; protected set; }
+    public double Capacity { get; protected set; }
+    protected Environment Environment { get; private set; }
+
+    protected Queue<ContainerPut> PutQueue { get; private set; }
+    protected Queue<ContainerGet> GetQueue { get; private set; }
+
+    public Container(Environment environment, double capacity = double.MaxValue, double initial = 0) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0", "capacity");
+      if (initial < 0) throw new ArgumentException("Initial must be >= 0", "initial");
+      if (initial > capacity) throw new ArgumentException("Initial must be <= capacity", "initial");
+      Environment = environment;
+      Capacity = capacity;
+      Level = initial;
+      PutQueue = new Queue<ContainerPut>();
+      GetQueue = new Queue<ContainerGet>();
+    }
+
+    public virtual ContainerPut Put(double amount) {
+      if (amount > Capacity) throw new ArgumentException("Cannot put more than capacity", "amount");
+      var put = new ContainerPut(Environment, TriggerGet, amount);
+      PutQueue.Enqueue(put);
+      TriggerPut();
+      return put;
+    }
+
+    public virtual ContainerGet Get(double amount) {
+      if (amount > Capacity) throw new ArgumentException("Cannot get more than capacity", "amount");
+      var get = new ContainerGet(Environment, TriggerPut, amount);
+      GetQueue.Enqueue(get);
+      TriggerGet();
+      return get;
+    }
+
+    protected virtual void DoPut(ContainerPut put) {
+      if (Capacity - Level >= put.Amount) {
+        Level += put.Amount;
+        put.Succeed();
+      }
+    }
+
+    protected virtual void DoGet(ContainerGet get) {
+      if (Level >= get.Amount) {
+        Level -= get.Amount;
+        get.Succeed();
+      }
+    }
+
+    protected virtual void TriggerPut(Event @event = null) {
+      while (PutQueue.Count > 0) {
+        var put = PutQueue.Peek();
+        DoPut(put);
+        if (put.IsTriggered) {
+          PutQueue.Dequeue();
+        } else break;
+      }
+    }
+
+    protected virtual void TriggerGet(Event @event = null) {
+      while (GetQueue.Count > 0) {
+        var get = GetQueue.Peek();
+        DoGet(get);
+        if (get.IsTriggered) {
+          GetQueue.Dequeue();
+        } else break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ContainerGet.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ContainerGet.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ContainerGet.cs	(revision 16240)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class ContainerGet : Event {
+    public double Amount { get; protected set; }
+    public DateTime Time { get; private set; }
+    public Process Process { get; private set; }
+
+    public ContainerGet(Environment environment, Action<Event> callback, double amount)
+      : base(environment) {
+      if (amount <= 0) throw new ArgumentException("Amount must be > 0.", "amount");
+      Amount = amount;
+      CallbackList.Add(callback);
+      Time = environment.Now;
+      Process = environment.ActiveProcess;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ContainerPut.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ContainerPut.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ContainerPut.cs	(revision 16240)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class ContainerPut : Event {
+    public double Amount { get; protected set; }
+    public DateTime Time { get; private set; }
+    public Process Process { get; private set; }
+
+    public ContainerPut(Environment environment, Action<Event> callback, double amount)
+      : base(environment) {
+      if (amount <= 0) throw new ArgumentException("Amount must be > 0.", "amount");
+      Amount = amount;
+      CallbackList.Add(callback);
+      Time = environment.Now;
+      Process = environment.ActiveProcess;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/FilterStoreGet.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/FilterStoreGet.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/FilterStoreGet.cs	(revision 16240)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class FilterStoreGet : StoreGet {
+    public Func<object, bool> Filter { get; private set; }
+
+    public FilterStoreGet(Environment environment, Action<Event> callback, Func<object, bool> filter)
+      : base(environment, callback) {
+      Filter = filter;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/PreemptiveRequest.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/PreemptiveRequest.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/PreemptiveRequest.cs	(revision 16240)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public sealed class PreemptiveRequest : PriorityRequest {
+    public bool Preempt { get; private set; }
+
+    public PreemptiveRequest(Environment environment, Action<Event> callback, Action<Event> disposeCallback, int priority = 1, bool preempt = false)
+      : base(environment, callback, disposeCallback, priority) {
+      Preempt = preempt;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/PriorityRequest.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/PriorityRequest.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/PriorityRequest.cs	(revision 16240)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class PriorityRequest : Request {
+    public int Priority { get; private set; }
+
+    public PriorityRequest(Environment environment, Action<Event> callback, Action<Event> disposeCallback, int priority = 1)
+      : base(environment, callback, disposeCallback) {
+      Priority = priority;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/Release.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/Release.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/Release.cs	(revision 16240)
@@ -0,0 +1,31 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public sealed class Release : Event {
+    public Request Request { get; private set; }
+
+    public Release(Environment environment, Request request, Action<Event> callback)
+      : base(environment) {
+      Request = request;
+      CallbackList.Add(callback);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/Request.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/Request.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/Request.cs	(revision 16240)
@@ -0,0 +1,39 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class Request : Event, IDisposable {
+    private readonly Action<Event> disposeCallback;
+    public DateTime Time { get; private set; }
+    public Process Process { get; private set; }
+
+    public Request(Environment environment, Action<Event> callback, Action<Event> disposeCallback)
+      : base(environment) {
+      CallbackList.Add(callback);
+      this.disposeCallback = disposeCallback;
+      Time = environment.Now;
+      Process = environment.ActiveProcess;
+    }
+
+    public virtual void Dispose() {
+      if (disposeCallback != null) disposeCallback(this);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ResourcePoolRequest.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ResourcePoolRequest.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/ResourcePoolRequest.cs	(revision 16240)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class ResourcePoolRequest : Request {
+    public Func<object, bool> Filter { get; private set; }
+
+    public ResourcePoolRequest(Environment environment, Action<Event> callback, Action<Event> disposeCallback, Func<object, bool> filter)
+      : base(environment, callback, disposeCallback) {
+      Filter = filter;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/StoreGet.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/StoreGet.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/StoreGet.cs	(revision 16240)
@@ -0,0 +1,33 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class StoreGet : Event {
+    public DateTime Time { get; private set; }
+    public Process Process { get; private set; }
+
+    public StoreGet(Environment environment, Action<Event> callback)
+      : base(environment) {
+      CallbackList.Add(callback);
+      Time = environment.Now;
+      Process = environment.ActiveProcess;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/StorePut.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/StorePut.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Events/StorePut.cs	(revision 16240)
@@ -0,0 +1,35 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class StorePut : Event {
+    public DateTime Time { get; private set; }
+    public Process Process { get; private set; }
+
+    public StorePut(Environment environment, Action<Event> callback, object value)
+      : base(environment) {
+      if (value == null) throw new ArgumentNullException("value", "Value to put in a Store cannot be null.");
+      CallbackList.Add(callback);
+      Value = value;
+      Time = environment.Now;
+      Process = environment.ActiveProcess;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/FilterStore.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/FilterStore.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/FilterStore.cs	(revision 16240)
@@ -0,0 +1,111 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimSharp {
+  public class FilterStore {
+    public int Count { get { return Items.Count; } }
+    public int Capacity { get; protected set; }
+    protected Environment Environment { get; private set; }
+
+    protected Queue<StorePut> PutQueue { get; private set; }
+    protected LinkedList<FilterStoreGet> GetQueue { get; private set; }
+    protected List<object> Items { get; private set; }
+
+    public FilterStore(Environment environment, int capacity = int.MaxValue) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0", "capacity");
+      Environment = environment;
+      Capacity = capacity;
+      PutQueue = new Queue<StorePut>();
+      GetQueue = new LinkedList<FilterStoreGet>();
+      Items = new List<object>();
+    }
+    public FilterStore(Environment environment, IEnumerable<object> items, int capacity = int.MaxValue) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0", "capacity");
+      Environment = environment;
+      Capacity = capacity;
+      PutQueue = new Queue<StorePut>();
+      GetQueue = new LinkedList<FilterStoreGet>();
+      Items = new List<object>(items);
+      if (capacity < Items.Count) throw new ArgumentException("There are more initial items than there is capacity.", "items");
+    }
+
+    public virtual bool IsAvailable(Func<object, bool> filter) {
+      return Items.Any(filter);
+    }
+
+    public virtual StorePut Put(object item) {
+      var put = new StorePut(Environment, TriggerGet, item);
+      PutQueue.Enqueue(put);
+      TriggerPut();
+      return put;
+    }
+
+    public virtual FilterStoreGet Get(Func<object, bool> filter = null) {
+      if (filter == null) filter = _ => true;
+      var get = new FilterStoreGet(Environment, TriggerPut, filter);
+      GetQueue.AddLast(get);
+      TriggerGet();
+      return get;
+    }
+
+    protected virtual void DoPut(StorePut put) {
+      if (Items.Count < Capacity) {
+        Items.Add(put.Value);
+        put.Succeed();
+      }
+    }
+
+    protected virtual void DoGet(FilterStoreGet get) {
+      for (int i = 0; i < Items.Count; i++) {
+        var item = Items[i];
+        if (!get.Filter(item)) continue;
+        Items.RemoveAt(i);
+        get.Succeed(item);
+        return;
+      }
+    }
+
+    protected virtual void TriggerPut(Event @event = null) {
+      while (PutQueue.Count > 0) {
+        var put = PutQueue.Peek();
+        DoPut(put);
+        if (put.IsTriggered) {
+          PutQueue.Dequeue();
+        } else break;
+      }
+    }
+
+    protected virtual void TriggerGet(Event @event = null) {
+      var current = GetQueue.First;
+      while (current != null) {
+        var get = current.Value;
+        DoGet(get);
+        if (get.IsTriggered) {
+          var next = current.Next;
+          GetQueue.Remove(current);
+          current = next;
+        } else current = current.Next;
+        if (Items.Count == 0) break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/PreemptiveResource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/PreemptiveResource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/PreemptiveResource.cs	(revision 16240)
@@ -0,0 +1,129 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimSharp {
+  public class PreemptiveResource {
+
+    public int Capacity { get; protected set; }
+
+    public int InUse { get { return Users.Count; } }
+
+    public int Remaining { get { return Capacity - InUse; } }
+
+    protected Environment Environment { get; private set; }
+
+    protected SortedList<int, LinkedList<PreemptiveRequest>> RequestQueue { get; private set; }
+    protected Queue<Release> ReleaseQueue { get; private set; }
+    protected HashSet<Request> Users { get; private set; }
+
+    public PreemptiveResource(Environment environment, int capacity = 1) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0.", "capacity");
+      Environment = environment;
+      Capacity = capacity;
+      RequestQueue = new SortedList<int, LinkedList<PreemptiveRequest>>();
+      ReleaseQueue = new Queue<Release>();
+      Users = new HashSet<Request>();
+    }
+
+    public virtual PreemptiveRequest Request(int priority = 1, bool preempt = false) {
+      var request = new PreemptiveRequest(Environment, TriggerRelease, DisposeCallback, priority, preempt);
+      if (!RequestQueue.ContainsKey(request.Priority))
+        RequestQueue.Add(request.Priority, new LinkedList<PreemptiveRequest>());
+      RequestQueue[request.Priority].AddLast(request);
+      TriggerRequest();
+      return request;
+    }
+
+    public virtual Release Release(PreemptiveRequest request) {
+      var release = new Release(Environment, request, TriggerRequest);
+      ReleaseQueue.Enqueue(release);
+      TriggerRelease();
+      return release;
+    }
+
+    protected void DisposeCallback(Event @event) {
+      var request = @event as PreemptiveRequest;
+      if (request != null) Release(request);
+    }
+
+    protected virtual void DoRequest(PreemptiveRequest request) {
+      if (Users.Count >= Capacity && request.Preempt) {
+        // Check if we can preempt another process
+        var oldest = Users.OfType<PreemptiveRequest>().Select((r, i) => new { Request = r, Index = i })
+          .OrderByDescending(x => x.Request.Priority)
+          .ThenByDescending(x => x.Request.Time)
+          .ThenByDescending(x => x.Request.Preempt)
+          .ThenByDescending(x => x.Index)
+          .First().Request;
+        if (oldest.Priority > request.Priority || (oldest.Priority == request.Priority
+            && (!oldest.Preempt && request.Preempt || (oldest.Preempt == request.Preempt
+              && oldest.Time > request.Time)))) {
+          Users.Remove(oldest);
+          oldest.Process.Interrupt(new Preempted(request.Process, oldest.Time));
+        }
+      }
+      if (Users.Count < Capacity) {
+        Users.Add(request);
+        request.Succeed();
+      }
+    }
+
+    protected virtual void DoRelease(Release release) {
+      if (!Users.Remove(release.Request)) {
+        var preemptRequest = release.Request as PreemptiveRequest;
+        if (preemptRequest != null) {
+          var current = RequestQueue[preemptRequest.Priority].First;
+          while (current != null && current.Value != release.Request)
+            current = current.Next;
+          if (current != null) RequestQueue[preemptRequest.Priority].Remove(current);
+        }
+      }
+      release.Succeed();
+    }
+
+    protected virtual void TriggerRequest(Event @event = null) {
+      foreach (var entry in RequestQueue) {
+        var requests = entry.Value;
+        var current = requests.First;
+        while (current != null) {
+          var request = current.Value;
+          DoRequest(request);
+          if (request.IsTriggered) {
+            var next = current.Next;
+            requests.Remove(current);
+            current = next;
+          } else current = current.Next;
+        }
+      }
+    }
+
+    protected virtual void TriggerRelease(Event @event = null) {
+      while (ReleaseQueue.Count > 0) {
+        var release = ReleaseQueue.Peek();
+        DoRelease(release);
+        if (release.IsTriggered) {
+          ReleaseQueue.Dequeue();
+        } else break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/PriorityResource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/PriorityResource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/PriorityResource.cs	(revision 16240)
@@ -0,0 +1,115 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace SimSharp {
+  public class PriorityResource {
+
+    public int Capacity { get; protected set; }
+
+    public int InUse { get { return Users.Count; } }
+
+    public int Remaining { get { return Capacity - InUse; } }
+
+    protected Environment Environment { get; private set; }
+
+    protected SortedList<int, LinkedList<PriorityRequest>> RequestQueue { get; private set; }
+    protected Queue<Release> ReleaseQueue { get; private set; }
+    protected HashSet<Request> Users { get; private set; }
+
+    public PriorityResource(Environment environment, int capacity = 1) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0.", "capacity");
+      Environment = environment;
+      Capacity = capacity;
+      RequestQueue = new SortedList<int, LinkedList<PriorityRequest>>();
+      ReleaseQueue = new Queue<Release>();
+      Users = new HashSet<Request>();
+    }
+
+    public virtual PriorityRequest Request(int priority = 1) {
+      var request = new PriorityRequest(Environment, TriggerRelease, DisposeCallback, priority);
+      if (!RequestQueue.ContainsKey(priority))
+        RequestQueue.Add(priority, new LinkedList<PriorityRequest>());
+      RequestQueue[priority].AddLast(request);
+      TriggerRequest();
+      return request;
+    }
+
+    public virtual Release Release(PriorityRequest request) {
+      var release = new Release(Environment, request, TriggerRequest);
+      ReleaseQueue.Enqueue(release);
+      TriggerRelease();
+      return release;
+    }
+
+    protected void DisposeCallback(Event @event) {
+      var request = @event as PriorityRequest;
+      if (request != null) Release(request);
+    }
+
+    protected virtual void DoRequest(Request request) {
+      if (Users.Count < Capacity) {
+        Users.Add(request);
+        request.Succeed();
+      }
+    }
+
+    protected virtual void DoRelease(Release release) {
+      if (!Users.Remove(release.Request)) {
+        var prioRequest = release.Request as PriorityRequest;
+        if (prioRequest != null) {
+          var current = RequestQueue[prioRequest.Priority].First;
+          while (current != null && current.Value != release.Request)
+            current = current.Next;
+          if (current != null) RequestQueue[prioRequest.Priority].Remove(current);
+        }
+      }
+      release.Succeed();
+    }
+
+    protected virtual void TriggerRequest(Event @event = null) {
+      foreach (var entry in RequestQueue) {
+        var cascade = false;
+        var requests = entry.Value;
+        while (requests.Count > 0) {
+          var req = requests.First.Value;
+          DoRequest(req);
+          if (req.IsTriggered) {
+            requests.RemoveFirst();
+          } else {
+            cascade = true;
+            break;
+          }
+        }
+        if (cascade) break;
+      }
+    }
+
+    protected virtual void TriggerRelease(Event @event = null) {
+      while (ReleaseQueue.Count > 0) {
+        var release = ReleaseQueue.Peek();
+        DoRelease(release);
+        if (release.IsTriggered) {
+          ReleaseQueue.Dequeue();
+        } else break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Resource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Resource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Resource.cs	(revision 16240)
@@ -0,0 +1,104 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace SimSharp {
+  public class Resource {
+
+    public int Capacity { get; protected set; }
+
+    public int InUse { get { return Users.Count; } }
+
+    public int Remaining { get { return Capacity - InUse; } }
+
+    protected Environment Environment { get; private set; }
+
+    protected LinkedList<Request> RequestQueue { get; private set; }
+    protected Queue<Release> ReleaseQueue { get; private set; }
+    protected HashSet<Request> Users { get; private set; }
+
+    public Resource(Environment environment, int capacity = 1) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must > 0.", "capacity");
+      Environment = environment;
+      Capacity = capacity;
+      RequestQueue = new LinkedList<Request>();
+      ReleaseQueue = new Queue<Release>();
+      Users = new HashSet<Request>();
+    }
+
+    public virtual Request Request() {
+      var request = new Request(Environment, TriggerRelease, DisposeCallback);
+      RequestQueue.AddLast(request);
+      TriggerRequest();
+      return request;
+    }
+
+    public virtual Release Release(Request request) {
+      var release = new Release(Environment, request, TriggerRequest);
+      ReleaseQueue.Enqueue(release);
+      TriggerRelease();
+      return release;
+    }
+
+    protected virtual void DisposeCallback(Event @event) {
+      var request = @event as Request;
+      if (request != null) {
+        Release(request);
+      }
+    }
+
+    protected virtual void DoRequest(Request request) {
+      if (Users.Count < Capacity) {
+        Users.Add(request);
+        request.Succeed();
+      }
+    }
+
+    protected virtual void DoRelease(Release release) {
+      if (!Users.Remove(release.Request)) {
+        var current = RequestQueue.First;
+        while (current != null && current.Value != release.Request)
+          current = current.Next;
+        if (current != null) RequestQueue.Remove(current);
+      }
+      release.Succeed();
+    }
+
+    protected virtual void TriggerRequest(Event @event = null) {
+      while (RequestQueue.Count > 0) {
+        var request = RequestQueue.First.Value;
+        DoRequest(request);
+        if (request.IsTriggered) {
+          RequestQueue.RemoveFirst();
+        } else break;
+      }
+    }
+
+    protected virtual void TriggerRelease(Event @event = null) {
+      while (ReleaseQueue.Count > 0) {
+        var release = ReleaseQueue.Peek();
+        DoRelease(release);
+        if (release.IsTriggered) {
+          ReleaseQueue.Dequeue();
+        } else break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/ResourcePool.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/ResourcePool.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/ResourcePool.cs	(revision 16240)
@@ -0,0 +1,110 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimSharp {
+  public class ResourcePool {
+    protected static readonly Func<object, bool> TrueFunc = _ => true;
+
+    public int Capacity { get; protected set; }
+
+    public int InUse { get { return Capacity - Remaining; } }
+
+    public int Remaining { get { return Resources.Count; } }
+
+    protected Environment Environment { get; private set; }
+
+    protected LinkedList<ResourcePoolRequest> RequestQueue { get; private set; }
+    protected Queue<Release> ReleaseQueue { get; private set; }
+    protected List<object> Resources { get; private set; }
+
+    public ResourcePool(Environment environment, IEnumerable<object> resources) {
+      Environment = environment;
+      if (resources == null) throw new ArgumentNullException("resources");
+      Resources = new List<object>(resources);
+      Capacity = Resources.Count;
+      if (Capacity == 0) throw new ArgumentException("There must be at least one resource", "resources");
+      RequestQueue = new LinkedList<ResourcePoolRequest>();
+      ReleaseQueue = new Queue<Release>();
+    }
+
+    public virtual bool IsAvailable(Func<object, bool> filter) {
+      return Resources.Any(filter);
+    }
+
+    public virtual ResourcePoolRequest Request(Func<object, bool> filter = null) {
+      var request = new ResourcePoolRequest(Environment, TriggerRelease, DisposeCallback, filter ?? TrueFunc);
+      RequestQueue.AddLast(request);
+      TriggerRequest();
+      return request;
+    }
+
+    public virtual Release Release(Request request) {
+      var release = new Release(Environment, request, TriggerRequest);
+      ReleaseQueue.Enqueue(release);
+      TriggerRelease();
+      return release;
+    }
+
+    protected virtual void DisposeCallback(Event @event) {
+      var request = @event as Request;
+      if (request != null) Release(request);
+    }
+
+    protected virtual void DoRequest(ResourcePoolRequest request) {
+      foreach (var o in Resources) {
+        if (!request.Filter(o)) continue;
+        Resources.Remove(o);
+        request.Succeed(o);
+        return;
+      }
+    }
+
+    protected virtual void DoRelease(Release release) {
+      Resources.Add(release.Request.Value);
+      release.Succeed();
+    }
+
+    protected virtual void TriggerRequest(Event @event = null) {
+      var current = RequestQueue.First;
+      while (current != null) {
+        var request = current.Value;
+        DoRequest(request);
+        if (request.IsTriggered) {
+          var next = current.Next;
+          RequestQueue.Remove(current);
+          current = next;
+        } else current = current.Next;
+        if (Resources.Count == 0) break;
+      }
+    }
+
+    protected virtual void TriggerRelease(Event @event = null) {
+      while (ReleaseQueue.Count > 0) {
+        var release = ReleaseQueue.Peek();
+        DoRelease(release);
+        if (release.IsTriggered) {
+          ReleaseQueue.Dequeue();
+        } else break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Store.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Store.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Resources/Store.cs	(revision 16240)
@@ -0,0 +1,91 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimSharp {
+  public class Store {
+    public int Count { get { return Items.Count; } }
+    public int Capacity { get; protected set; }
+    protected Environment Environment { get; private set; }
+
+    protected Queue<StorePut> PutQueue { get; private set; }
+    protected Queue<StoreGet> GetQueue { get; private set; }
+    protected List<object> Items { get; private set; }
+
+    public Store(Environment environment, int capacity = int.MaxValue) {
+      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0", "capacity");
+      Environment = environment;
+      Capacity = capacity;
+      PutQueue = new Queue<StorePut>();
+      GetQueue = new Queue<StoreGet>();
+      Items = new List<object>();
+    }
+
+    public virtual StorePut Put(object item) {
+      var put = new StorePut(Environment, TriggerGet, item);
+      PutQueue.Enqueue(put);
+      TriggerPut();
+      return put;
+    }
+
+    public virtual StoreGet Get() {
+      var get = new StoreGet(Environment, TriggerPut);
+      GetQueue.Enqueue(get);
+      TriggerGet();
+      return get;
+    }
+
+    protected virtual void DoPut(StorePut put) {
+      if (Items.Count < Capacity) {
+        Items.Add(put.Value);
+        put.Succeed();
+      }
+    }
+
+    protected virtual void DoGet(StoreGet get) {
+      if (Items.Count > 0) {
+        var item = Items.First();
+        Items.RemoveAt(0);
+        get.Succeed(item);
+      }
+    }
+
+    protected virtual void TriggerPut(Event @event = null) {
+      while (PutQueue.Count > 0) {
+        var put = PutQueue.Peek();
+        DoPut(put);
+        if (put.IsTriggered) {
+          PutQueue.Dequeue();
+        } else break;
+      }
+    }
+
+    protected virtual void TriggerGet(Event @event = null) {
+      while (GetQueue.Count > 0) {
+        var get = GetQueue.Peek();
+        DoGet(get);
+        if (get.IsTriggered) {
+          GetQueue.Dequeue();
+        } else break;
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Properties/AssemblyInfo.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Properties/AssemblyInfo.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Properties/AssemblyInfo.cs	(revision 16240)
@@ -0,0 +1,53 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SimSharp")]
+[assembly: AssemblyDescription("A .NET port of SimPy, discrete event simulation framework")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HEAL")]
+[assembly: AssemblyProduct("SimSharp")]
+[assembly: AssemblyCopyright("(c) 2002-2018 HEAL")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d51e9cc2-c26b-4df5-94f8-ca8f63f75db9")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("3.0.11.0")]
+[assembly: AssemblyFileVersion("3.0.11.0")]
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/FastRandom.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/FastRandom.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/FastRandom.cs	(revision 16240)
@@ -0,0 +1,413 @@
+﻿/* ***************************************************************************
+ * This file is part of SharpNEAT - Evolution of Neural Networks.
+ * 
+ * Copyright 2004-2006, 2009-2010 Colin Green (sharpneat@gmail.com)
+ *
+ * SharpNEAT is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SharpNEAT is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SharpNEAT.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// ENHANCEMENT: Replace usages of this class with the superceding version from Math.Net.
+using System;
+
+namespace SimSharp {
+  /// <summary>
+  /// A fast random number generator for .NET
+  /// Colin Green, January 2005
+  /// 
+  /// Key points:
+  ///  1) Based on a simple and fast xor-shift pseudo random number generator (RNG) specified in: 
+  ///  Marsaglia, George. (2003). Xorshift RNGs.
+  ///  http://www.jstatsoft.org/v08/i14/paper
+  ///  
+  ///  This particular implementation of xorshift has a period of 2^128-1. See the above paper to see
+  ///  how this can be easily extened if you need a longer period. At the time of writing I could find no 
+  ///  information on the period of System.Random for comparison.
+  /// 
+  ///  2) Faster than System.Random. Up to 8x faster, depending on which methods are called.
+  /// 
+  ///  3) Direct replacement for System.Random. This class implements all of the methods that System.Random 
+  ///  does plus some additional methods. The like named methods are functionally equivalent.
+  ///  
+  ///  4) Allows fast re-initialisation with a seed, unlike System.Random which accepts a seed at construction
+  ///  time which then executes a relatively expensive initialisation routine. This provides a vast speed improvement
+  ///  if you need to reset the pseudo-random number sequence many times, e.g. if you want to re-generate the same
+  ///  sequence of random numbers many times. An alternative might be to cache random numbers in an array, but that 
+  ///  approach is limited by memory capacity and the fact that you may also want a large number of different sequences 
+  ///  cached. Each sequence can be represented by a single seed value (int) when using FastRandom.
+  ///  
+  ///  Notes.
+  ///  A further performance improvement can be obtained by declaring local variables as static, thus avoiding 
+  ///  re-allocation of variables on each call. However care should be taken if multiple instances of
+  ///  FastRandom are in use or if being used in a multi-threaded environment.
+  /// 
+  /// 
+  /// Colin Green, September 4th 2005
+  ///   - Added NextBytesUnsafe() - commented out by default.
+  ///   - Fixed bug in Reinitialise() - y,z and w variables were not being reset.
+  ///     
+  /// Colin Green, December 2008.
+  ///   - Fix to Next() - Was previously able to return int.MaxValue, contrary to the method's contract and comments.
+  ///   - Modified NextBool() to use _bitMask instead of a count of remaining bits. Also reset the bit buffer in Reinitialise().
+  ///    
+  /// Colin Green, 2011-08-31
+  ///   - Added NextByte() method.
+  ///   - Added new statically declared seedRng FastRandom to allow easy creation of multiple FastRandoms with different seeds 
+  ///     within a single clock tick.
+  ///     
+  /// Colin Green, 2011-10-04
+  ///  - Seeds are now hashed. Without this the first random sample for nearby seeds (1,2,3, etc.) are very similar 
+  ///    (have a similar bit pattern). Thanks to Francois Guibert for identifying this problem.
+  /// 
+  /// </summary>
+  public class FastRandom : IRandom {
+    #region Static Fields
+    /// <summary>
+    /// A static RNG that is used to generate seed values when constructing new instances of FastRandom.
+    /// This overcomes the problem whereby multiple FastRandom instances are instantiated within the same
+    /// tick count and thus obtain the same seed, that approach can result in extreme biases occuring 
+    /// in some cases depending on how the RNG is used.
+    /// </summary>
+    static readonly FastRandom __seedRng = new FastRandom((int)System.Environment.TickCount);
+    #endregion
+
+    #region Instance Fields
+
+    // The +1 ensures NextDouble doesn't generate 1.0
+    const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
+    const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
+    const uint Y = 842502087, Z = 3579807591, W = 273326509;
+
+    uint _x, _y, _z, _w;
+
+    #endregion
+
+    #region Constructors
+
+    /// <summary>
+    /// Initialises a new instance using a seed generated from the class's static seed RNG.
+    /// </summary>
+    public FastRandom() {
+      Reinitialise(__seedRng.NextInt());
+    }
+
+    /// <summary>
+    /// Initialises a new instance using an int value as seed.
+    /// This constructor signature is provided to maintain compatibility with
+    /// System.Random
+    /// </summary>
+    public FastRandom(int seed) {
+      Reinitialise(seed);
+    }
+
+    #endregion
+
+    #region Public Methods [Reinitialisation]
+
+    /// <summary>
+    /// Reinitialises using an int value as a seed.
+    /// </summary>
+    public void Reinitialise(int seed) {
+      // The only stipulation stated for the xorshift RNG is that at least one of
+      // the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
+      // resetting of the x seed.
+
+      // The first random sample will be very closely related to the value of _x we set here. 
+      // Thus setting _x = seed will result in a close correlation between the bit patterns of the seed and
+      // the first random sample, therefore if the seed has a pattern (e.g. 1,2,3) then there will also be 
+      // a recognisable pattern across the first random samples.
+      //
+      // Such a strong correlation between the seed and the first random sample is an undesirable
+      // charactersitic of a RNG, therefore we significantly weaken any correlation by hashing the seed's bits. 
+      // This is achieved by multiplying the seed with four large primes each with bits distributed over the
+      // full length of a 32bit value, finally adding the results to give _x.
+      _x = (uint)((seed * 1431655781)
+                  + (seed * 1183186591)
+                  + (seed * 622729787)
+                  + (seed * 338294347));
+
+      _y = Y;
+      _z = Z;
+      _w = W;
+
+      _bitBuffer = 0;
+      _bitMask = 1;
+    }
+
+    #endregion
+
+    #region Public Methods [System.Random functionally equivalent methods]
+
+    /// <summary>
+    /// Generates a random int over the range 0 to int.MaxValue-1.
+    /// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
+    /// This does slightly eat into some of the performance gain over System.Random, but not much.
+    /// For better performance see:
+    /// 
+    /// Call NextInt() for an int over the range 0 to int.MaxValue.
+    /// 
+    /// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
+    /// including negative values. 
+    /// </summary>
+    public int Next() {
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+      _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
+
+      // Handle the special case where the value int.MaxValue is generated. This is outside of 
+      // the range of permitted values, so we therefore call Next() to try again.
+      uint rtn = _w & 0x7FFFFFFF;
+      if (rtn == 0x7FFFFFFF) {
+        return Next();
+      }
+      return (int)rtn;
+    }
+
+    /// <summary>
+    /// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
+    /// </summary>
+    public int Next(int upperBound) {
+      if (upperBound < 0) {
+        throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
+      }
+
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+
+      // ENHANCEMENT: Can we do this without converting to a double and back again?
+      // The explicit int cast before the first multiplication gives better performance.
+      // See comments in NextDouble.
+      return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
+    }
+
+    /// <summary>
+    /// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
+    /// upperBound must be >= lowerBound. lowerBound may be negative.
+    /// </summary>
+    public int Next(int lowerBound, int upperBound) {
+      if (lowerBound > upperBound) {
+        throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
+      }
+
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+
+      // The explicit int cast before the first multiplication gives better performance.
+      // See comments in NextDouble.
+      int range = upperBound - lowerBound;
+      if (range < 0) {   // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
+        // We also must use all 32 bits of precision, instead of the normal 31, which again is slower.  
+        return lowerBound + (int)((REAL_UNIT_UINT * (double)(_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
+      }
+
+      // 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
+      // a little more performance.
+      return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
+    }
+
+    /// <summary>
+    /// Generates a random double. Values returned are over the range [0, 1). That is, inclusive of 0.0 and exclusive of 1.0.
+    /// </summary>
+    public double NextDouble() {
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+
+      // Here we can gain a 2x speed improvement by generating a value that can be cast to 
+      // an int instead of the more easily available uint. If we then explicitly cast to an 
+      // int the compiler will then cast the int to a double to perform the multiplication, 
+      // this final cast is a lot faster than casting from a uint to a double. The extra cast
+      // to an int is very fast (the allocated bits remain the same) and so the overall effect 
+      // of the extra cast is a significant performance improvement.
+      //
+      // Also note that the loss of one bit of precision is equivalent to what occurs within 
+      // System.Random.
+      return REAL_UNIT_INT * (int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))));
+    }
+
+    /// <summary>
+    /// Fills the provided byte array with random bytes.
+    /// This method is functionally equivalent to System.Random.NextBytes(). 
+    /// </summary>
+    public void NextBytes(byte[] buffer) {
+      // Fill up the bulk of the buffer in chunks of 4 bytes at a time.
+      uint x = this._x, y = this._y, z = this._z, w = this._w;
+      int i = 0;
+      uint t;
+      for (int bound = buffer.Length - 3; i < bound; ) {
+        // Generate 4 bytes. 
+        // Increased performance is achieved by generating 4 random bytes per loop.
+        // Also note that no mask needs to be applied to zero out the higher order bytes before
+        // casting because the cast ignores thos bytes. Thanks to Stefan Trosch�tz for pointing this out.
+        t = x ^ (x << 11);
+        x = y; y = z; z = w;
+        w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+
+        buffer[i++] = (byte)w;
+        buffer[i++] = (byte)(w >> 8);
+        buffer[i++] = (byte)(w >> 16);
+        buffer[i++] = (byte)(w >> 24);
+      }
+
+      // Fill up any remaining bytes in the buffer.
+      if (i < buffer.Length) {
+        // Generate 4 bytes.
+        t = x ^ (x << 11);
+        x = y; y = z; z = w;
+        w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+
+        buffer[i++] = (byte)w;
+        if (i < buffer.Length) {
+          buffer[i++] = (byte)(w >> 8);
+          if (i < buffer.Length) {
+            buffer[i++] = (byte)(w >> 16);
+            if (i < buffer.Length) {
+              buffer[i] = (byte)(w >> 24);
+            }
+          }
+        }
+      }
+      this._x = x; this._y = y; this._z = z; this._w = w;
+    }
+
+    ///// <summary>
+    ///// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
+    ///// thus providing a nice speedup. The loop is also partially unrolled to allow out-of-order-execution,
+    ///// this results in about a x2 speedup on an AMD Athlon. Thus performance may vary wildly on different CPUs
+    ///// depending on the number of execution units available.
+    ///// 
+    ///// Another significant speedup is obtained by setting the 4 bytes by indexing pDWord (e.g. pDWord[i++]=_w)
+    ///// instead of dereferencing it (e.g. *pDWord++=_w).
+    ///// 
+    ///// Note that this routine requires the unsafe compilation flag to be specified and so is commented out by default.
+    ///// </summary>
+    ///// <param name="buffer"></param>
+    //      public unsafe void NextBytesUnsafe(byte[] buffer)
+    //      {
+    //          if(buffer.Length % 8 != 0)
+    //              throw new ArgumentException("Buffer length must be divisible by 8", "buffer");
+    //
+    //          uint _x=this._x, _y=this._y, _z=this._z, _w=this._w;
+    //          
+    //          fixed(byte* pByte0 = buffer)
+    //          {
+    //              uint* pDWord = (uint*)pByte0;
+    //              for(int i=0, len=buffer.Length>>2; i < len; i+=2) 
+    //              {
+    //                  uint t=(_x^(_x<<11));
+    //                  _x=_y; _y=_z; _z=_w;
+    //                  pDWord[i] = _w = (_w^(_w>>19))^(t^(t>>8));
+    //
+    //                  t=(_x^(_x<<11));
+    //                  _x=_y; _y=_z; _z=_w;
+    //                  pDWord[i+1] = _w = (_w^(_w>>19))^(t^(t>>8));
+    //              }
+    //          }
+    //
+    //          this._x=_x; this._y=_y; this._z=_z; this._w=_w;
+    //      }
+    #endregion
+
+    #region Public Methods [Methods not present on System.Random]
+
+    /// <summary>
+    /// Generates a uint. Values returned are over the full range of a uint, 
+    /// uint.MinValue to uint.MaxValue, inclusive.
+    /// 
+    /// This is the fastest method for generating a single random number because the underlying
+    /// random number generator algorithm generates 32 random bits that can be cast directly to 
+    /// a uint.
+    /// </summary>
+    public uint NextUInt() {
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+      return _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
+    }
+
+    /// <summary>
+    /// Generates a random int over the range 0 to int.MaxValue, inclusive. 
+    /// This method differs from Next() only in that the range is 0 to int.MaxValue
+    /// and not 0 to int.MaxValue-1.
+    /// 
+    /// The slight difference in range means this method is slightly faster than Next()
+    /// but is not functionally equivalent to System.Random.Next().
+    /// </summary>
+    public int NextInt() {
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+      return (int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))));
+    }
+
+    /// <summary>
+    /// Generates a random double. Values returned are over the range (0, 1). That is, exclusive of both 0.0 and 1.0.
+    /// </summary>
+    public double NextDoubleNonZero() {
+      uint t = _x ^ (_x << 11);
+      _x = _y; _y = _z; _z = _w;
+
+      // See notes on NextDouble(). Here we generate a random value from 0 to 0x7f ff ff fe, and add one
+      // to generate a random value from 1 to 0x7f ff ff ff.
+      return REAL_UNIT_INT * (int)((0x7FFFFFFE & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8)))) + 1U);
+    }
+
+    // Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
+    // with bitMask.
+    uint _bitBuffer;
+    uint _bitMask;
+
+    /// <summary>
+    /// Generates a single random bit.
+    /// This method's performance is improved by generating 32 bits in one operation and storing them
+    /// ready for future calls.
+    /// </summary>
+    public bool NextBool() {
+      if (0 == _bitMask) {
+        // Generate 32 more bits.
+        uint t = _x ^ (_x << 11);
+        _x = _y; _y = _z; _z = _w;
+        _bitBuffer = _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
+
+        // Reset the bitMask that tells us which bit to read next.
+        _bitMask = 0x80000000;
+        return (_bitBuffer & _bitMask) == 0;
+      }
+
+      return (_bitBuffer & (_bitMask >>= 1)) == 0;
+    }
+
+    // Buffer of random bytes. A single UInt32 is used to buffer 4 bytes.
+    // _byteBufferState tracks how bytes remain in the buffer, a value of 
+    // zero  indicates that the buffer is empty.
+    uint _byteBuffer;
+    byte _byteBufferState;
+
+    /// <summary>
+    /// Generates a signle random byte with range [0,255].
+    /// This method's performance is improved by generating 4 bytes in one operation and storing them
+    /// ready for future calls.
+    /// </summary>
+    public byte NextByte() {
+      if (0 == _byteBufferState) {
+        // Generate 4 more bytes.
+        uint t = _x ^ (_x << 11);
+        _x = _y; _y = _z; _z = _w;
+        _byteBuffer = _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
+        _byteBufferState = 0x4;
+        return (byte)_byteBuffer;  // Note. Masking with 0xFF is unnecessary.
+      }
+      _byteBufferState >>= 1;
+      return (byte)(_byteBuffer >>= 1);
+    }
+
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/IRandom.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/IRandom.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/IRandom.cs	(revision 16240)
@@ -0,0 +1,28 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+namespace SimSharp {
+  public interface IRandom {
+    int Next();
+    int Next(int upperBound);
+    int Next(int lowerBound, int upperBound);
+    double NextDouble();
+
+    void Reinitialise(int seed);
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/SystemRandom.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/SystemRandom.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Random/SystemRandom.cs	(revision 16240)
@@ -0,0 +1,52 @@
+﻿#region License Information
+/* SimSharp - A .NET port of SimPy, discrete event simulation framework
+Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
+#endregion
+
+using System;
+
+namespace SimSharp {
+  public class SystemRandom : IRandom {
+    private Random random;
+
+    public SystemRandom() {
+      random = new Random();
+    }
+
+    public SystemRandom(int seed) {
+      random = new Random(seed);
+    }
+    public int Next() {
+      return random.Next();
+    }
+
+    public int Next(int upperBound) {
+      return random.Next(upperBound);
+    }
+
+    public int Next(int lowerBound, int upperBound) {
+      return random.Next(lowerBound, upperBound);
+    }
+
+    public double NextDouble() {
+      return random.NextDouble();
+    }
+
+    public void Reinitialise(int seed) {
+      random = new Random(seed);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/SimSharp-3.0.11.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/SimSharp-3.0.11.csproj	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/SimSharp-3.0.11.csproj	(revision 16240)
@@ -0,0 +1,133 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{9FE9C740-7859-4D01-AD07-7D4E15A6320B}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SimSharp</RootNamespace>
+    <AssemblyName>SimSharp-3.0.11</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+    <SignAssembly>true</SignAssembly>
+    <AssemblyOriginatorKeyFile>HeuristicLab.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\..\..\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
+    <OutputPath>..\..\..\..\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <DocumentationFile>
+    </DocumentationFile>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>..\..\..\..\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>..\..\..\..\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <DocumentationFile>
+    </DocumentationFile>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>..\..\..\..\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>..\..\..\..\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <DocumentationFile>
+    </DocumentationFile>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Analysis\ContinuousStatistics.cs" />
+    <Compile Include="Collections\EventQueue.cs" />
+    <Compile Include="Collections\EventQueueNode.cs" />
+    <Compile Include="Core\ActiveObject.cs" />
+    <Compile Include="Core\Events\AnyOf.cs" />
+    <Compile Include="Core\Events\AllOf.cs" />
+    <Compile Include="Core\Resources\Container.cs" />
+    <Compile Include="Core\Environment.cs" />
+    <Compile Include="Core\Resources\Events\ResourcePoolRequest.cs" />
+    <Compile Include="Core\Resources\Events\FilterStoreGet.cs" />
+    <Compile Include="Core\Resources\Events\StorePut.cs" />
+    <Compile Include="Core\Resources\Events\StoreGet.cs" />
+    <Compile Include="Core\Resources\Events\ContainerGet.cs" />
+    <Compile Include="Core\Resources\Events\ContainerPut.cs" />
+    <Compile Include="Core\Events\Condition.cs" />
+    <Compile Include="Core\Events\Event.cs" />
+    <Compile Include="Core\Resources\Events\PreemptiveRequest.cs" />
+    <Compile Include="Core\Events\Process.cs" />
+    <Compile Include="Core\Resources\Events\Release.cs" />
+    <Compile Include="Core\Resources\Events\PriorityRequest.cs" />
+    <Compile Include="Core\Resources\Events\Request.cs" />
+    <Compile Include="Core\Events\Timeout.cs" />
+    <Compile Include="Core\Exceptions\EmptyScheduleException.cs" />
+    <Compile Include="Core\Preempted.cs" />
+    <Compile Include="Core\Resources\FilterStore.cs" />
+    <Compile Include="Core\Resources\PreemptiveResource.cs" />
+    <Compile Include="Core\Resources\PriorityResource.cs" />
+    <Compile Include="Core\Resources\Resource.cs" />
+    <Compile Include="Core\Resources\ResourcePool.cs" />
+    <Compile Include="Core\Resources\Store.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Random\FastRandom.cs" />
+    <Compile Include="Random\IRandom.cs" />
+    <Compile Include="Random\SystemRandom.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Collections\EventQueue-License.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="HeuristicLab.snk" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs	(revision 16240)
@@ -188,4 +188,5 @@
           TimeSpan.FromSeconds(15) });
       snapshotTimesIndex = 0;
+      snapshots = new RunCollection();
       Runs = new RunCollection { OptimizerName = Name };
       Initialize();
@@ -199,4 +200,5 @@
           TimeSpan.FromSeconds(15) });
       snapshotTimesIndex = 0;
+      snapshots = new RunCollection();
       Runs = new RunCollection { OptimizerName = Name };
       Initialize();
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Optimizer/3.3/MenuItems/UsernamePasswordMenuItem.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Optimizer/3.3/MenuItems/UsernamePasswordMenuItem.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Optimizer/3.3/MenuItems/UsernamePasswordMenuItem.cs	(revision 16240)
@@ -22,4 +22,5 @@
 using System.Collections.Generic;
 using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
 using HeuristicLab.Clients.Common;
 using HeuristicLab.MainForm;
@@ -43,4 +44,5 @@
       using (PasswordDialog dialog = new PasswordDialog()) {
         dialog.ShowDialog((IWin32Window)MainFormManager.MainForm);
+        UserInformation.Instance.Refresh();
       }
     }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/DerivativeCalculator.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/DerivativeCalculator.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/DerivativeCalculator.cs	(revision 16240)
@@ -0,0 +1,193 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class DerivativeCalculator {
+    public static ISymbolicExpressionTree Derive(ISymbolicExpressionTree tree, string variableName) {
+      var mainBranch = tree.Root.GetSubtree(0).GetSubtree(0);
+      var root = new ProgramRootSymbol().CreateTreeNode();
+      root.AddSubtree(new StartSymbol().CreateTreeNode());
+      var dTree = TreeSimplifier.GetSimplifiedTree(Derive(mainBranch, variableName));
+      // var dTree = Derive(mainBranch, variableName);
+      root.GetSubtree(0).AddSubtree(dTree);
+      return new SymbolicExpressionTree(root);
+    }
+
+    private static Constant constantSy = new Constant();
+    private static Addition addSy = new Addition();
+    private static Subtraction subSy = new Subtraction();
+    private static Multiplication mulSy = new Multiplication();
+    private static Division divSy = new Division();
+
+    public static ISymbolicExpressionTreeNode Derive(ISymbolicExpressionTreeNode branch, string variableName) {
+      if (branch.Symbol is Constant) {
+        return CreateConstant(0.0);
+      }
+      if (branch.Symbol is Variable) {
+        var varNode = branch as VariableTreeNode;
+        if (varNode.VariableName == variableName) {
+          return CreateConstant(varNode.Weight);
+        } else {
+          return CreateConstant(0.0);
+        }
+      }
+      if (branch.Symbol is Addition) {
+        var sum = addSy.CreateTreeNode();
+        foreach (var subTree in branch.Subtrees) {
+          sum.AddSubtree(Derive(subTree, variableName));
+        }
+        return sum;
+      }
+      if (branch.Symbol is Subtraction) {
+        var sum = subSy.CreateTreeNode();
+        foreach (var subTree in branch.Subtrees) {
+          sum.AddSubtree(Derive(subTree, variableName));
+        }
+        return sum;
+      }
+      if (branch.Symbol is Multiplication) {
+        // (f * g)' = f'*g + f*g'
+        // for multiple factors: (f * g * h)' = ((f*g) * h)' = (f*g)' * h + (f*g) * h' 
+
+        if (branch.SubtreeCount >= 2) {
+          var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+          var g = (ISymbolicExpressionTreeNode)branch.GetSubtree(1).Clone();
+          var fprime = Derive(f, variableName);
+          var gprime = Derive(g, variableName);
+          var fgPrime = Sum(Product(f, gprime), Product(fprime, g));
+          for (int i = 2; i < branch.SubtreeCount; i++) {
+            var fg = Product((ISymbolicExpressionTreeNode)f.Clone(), (ISymbolicExpressionTreeNode)g.Clone());
+            var h = (ISymbolicExpressionTreeNode)branch.GetSubtree(i).Clone();
+            var hPrime = Derive(h, variableName);
+            fgPrime = Sum(Product(fgPrime, h), Product(fg, hPrime));
+          }
+          return fgPrime;
+        } else throw new ArgumentException();
+      }
+      if (branch.Symbol is Division) {
+        // (f/g)' = (f'g - g'f) / g²
+        if (branch.SubtreeCount == 1) {
+          var g = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+          var gPrime = Product(CreateConstant(-1.0), Derive(g, variableName));
+          var sqrNode = new Square().CreateTreeNode();
+          sqrNode.AddSubtree(g);
+          return Div(gPrime, sqrNode);
+        } else if (branch.SubtreeCount == 2) {
+          var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+          var g = (ISymbolicExpressionTreeNode)branch.GetSubtree(1).Clone();
+          var fprime = Derive(f, variableName);
+          var gprime = Derive(g, variableName);
+          var sqrNode = new Square().CreateTreeNode();
+          sqrNode.AddSubtree((ISymbolicExpressionTreeNode)branch.GetSubtree(1).Clone());
+          return Div(Subtract(Product(fprime, g), Product(f, gprime)), sqrNode);
+        } else throw new NotSupportedException();
+      }
+      if (branch.Symbol is Logarithm) {
+        var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+        return Product(Div(CreateConstant(1.0), f), Derive(f, variableName));
+      }
+      if (branch.Symbol is Exponential) {
+        var f = (ISymbolicExpressionTreeNode)branch.Clone();
+        return Product(f, Derive(branch.GetSubtree(0), variableName));
+      }
+      if(branch.Symbol is Square) {
+        var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+        return Product(Product(CreateConstant(2.0), f), Derive(f, variableName));
+      }     
+      if(branch.Symbol is SquareRoot) {
+        var f = (ISymbolicExpressionTreeNode)branch.Clone();
+        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+        return Product(Div(CreateConstant(1.0), Product(CreateConstant(2.0), f)), Derive(u, variableName));
+      }
+      if (branch.Symbol is Sine) {
+        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+        var cos = (new Cosine()).CreateTreeNode();
+        cos.AddSubtree(u);
+        return Product(cos, Derive(u, variableName));
+      }
+      if (branch.Symbol is Cosine) {
+        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
+        var sin = (new Sine()).CreateTreeNode();
+        sin.AddSubtree(u);
+        return Product(CreateConstant(-1.0), Product(sin, Derive(u, variableName)));
+      }
+      throw new NotSupportedException(string.Format("Symbol {0} is not supported.", branch.Symbol));
+    }
+
+
+    private static ISymbolicExpressionTreeNode Product(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
+      var product = mulSy.CreateTreeNode();
+      product.AddSubtree(f);
+      product.AddSubtree(g);
+      return product;
+    }
+    private static ISymbolicExpressionTreeNode Div(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
+      var div = divSy.CreateTreeNode();
+      div.AddSubtree(f);
+      div.AddSubtree(g);
+      return div;
+    }
+
+    private static ISymbolicExpressionTreeNode Sum(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
+      var sum = addSy.CreateTreeNode();
+      sum.AddSubtree(f);
+      sum.AddSubtree(g);
+      return sum;
+    }
+    private static ISymbolicExpressionTreeNode Subtract(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
+      var sum = subSy.CreateTreeNode();
+      sum.AddSubtree(f);
+      sum.AddSubtree(g);
+      return sum;
+    }
+                          
+    private static ISymbolicExpressionTreeNode CreateConstant(double v) {
+      var constNode = (ConstantTreeNode)constantSy.CreateTreeNode();
+      constNode.Value = v;
+      return constNode;
+    }
+
+    public static bool IsCompatible(ISymbolicExpressionTree tree) {
+      var containsUnknownSymbol = (
+        from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
+        where
+          !(n.Symbol is Variable) &&
+          !(n.Symbol is Constant) &&
+          !(n.Symbol is Addition) &&
+          !(n.Symbol is Subtraction) &&
+          !(n.Symbol is Multiplication) &&
+          !(n.Symbol is Division) &&
+          !(n.Symbol is Logarithm) &&
+          !(n.Symbol is Exponential) &&
+          !(n.Symbol is Square) &&
+          !(n.Symbol is SquareRoot) &&
+          !(n.Symbol is Sine) &&
+          !(n.Symbol is Cosine) &&
+          !(n.Symbol is StartSymbol)
+        select n).Any();
+      return !containsUnknownSymbol;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/LinearModelToTreeConverter.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/LinearModelToTreeConverter.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/LinearModelToTreeConverter.cs	(revision 16240)
@@ -37,21 +37,27 @@
       string[] variableNames, double[] coefficients,
       double @const = 0) {
+
       if (factorCoefficients.Length == 0 && coefficients.Length == 0) throw new ArgumentException();
-      ISymbolicExpressionTree p1 = null;
+
+      // Create tree for double variables
+      ISymbolicExpressionTree tree = null;      
       if (coefficients.Length > 0) {
-        p1 = CreateTree(variableNames, new int[variableNames.Length], coefficients, @const);
-        if (factorCoefficients.Length == 0)
-          return p1;
+        tree = CreateTree(variableNames, new int[variableNames.Length], coefficients, @const);
+        if (factorCoefficients.Length == 0) return tree;
       }
+
+      // Create tree for string variables
+      ISymbolicExpressionTree factorTree = null;      
       if (factorCoefficients.Length > 0) {
-        var p2 = CreateTree(factors, factorCoefficients);
-        if (p1 == null) return p2;
+        factorTree = CreateTree(factors, factorCoefficients, @const);
+        if (tree == null) return factorTree;  
+      }
 
-        // combine
-        ISymbolicExpressionTreeNode add = p1.Root.GetSubtree(0).GetSubtree(0);
-        foreach (var binFactorNode in p2.IterateNodesPrefix().OfType<BinaryFactorVariableTreeNode>())
-          add.AddSubtree(binFactorNode);
-        return p1;
-      }
+      // Combine both trees
+      ISymbolicExpressionTreeNode add = tree.Root.GetSubtree(0).GetSubtree(0);
+      foreach (var binFactorNode in factorTree.IterateNodesPrefix().OfType<BinaryFactorVariableTreeNode>())
+        add.InsertSubtree(add.SubtreeCount - 1, binFactorNode);
+      return tree;
+
       throw new ArgumentException();
     }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/TreeSimplifier.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/TreeSimplifier.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/TreeSimplifier.cs	(revision 16240)
@@ -1236,4 +1236,6 @@
         // $ * 1.0 => $
         return a;
+      } else if (IsConstant(b) && ((ConstantTreeNode)b).Value.IsAlmost(0.0)) {
+        return MakeConstant(0);
       } else if (IsConstant(b) && IsVariableBase(a)) {
         // multiply constants into variables weights
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs	(revision 16240)
@@ -0,0 +1,243 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionHashExtensions {
+    public sealed class HashNode<T> : IComparable<HashNode<T>>, IEquatable<HashNode<T>> where T : class {
+      public T Data;
+      public int Arity;
+      public int Size;
+      public bool IsCommutative;
+
+      public bool Enabled;
+      public int HashValue;           // the initial (fixed) hash value for this individual node/data
+      public int CalculatedHashValue; // the calculated hash value (taking into account the children hash values)
+
+      public Action<HashNode<T>[], int> Simplify;
+      public IComparer<T> Comparer;
+
+      public bool IsChild => Arity == 0;
+
+      public HashNode(IComparer<T> comparer) {
+        Comparer = comparer;
+      }
+
+      private HashNode() { }
+
+      public int CompareTo(HashNode<T> other) {
+        var res = Comparer.Compare(Data, other.Data);
+        return res == 0 ? CalculatedHashValue.CompareTo(other.CalculatedHashValue) : res;
+      }
+
+      public override string ToString() {
+        return $"{Data} {Arity} {Size} {CalculatedHashValue} {Enabled}";
+      }
+
+      public bool Equals(HashNode<T> other) {
+        return CalculatedHashValue.Equals(other.CalculatedHashValue);
+      }
+
+      public override bool Equals(object obj) {
+        var other = obj as HashNode<T>;
+        if (other != null)
+          return Equals(other);
+        return base.Equals(obj);
+      }
+
+      public override int GetHashCode() {
+        return CalculatedHashValue;
+      }
+
+      public static bool operator ==(HashNode<T> a, HashNode<T> b) {
+        return a.Equals(b);
+      }
+
+      public static bool operator !=(HashNode<T> a, HashNode<T> b) {
+        return !a.Equals(b);
+      }
+    }
+
+    public static int ComputeHash<T>(this HashNode<T>[] nodes, int i) where T : class {
+      var node = nodes[i];
+      var hashes = new int[node.Arity + 1];
+      int idx = 0;
+      foreach (int c in nodes.IterateChildren(i)) {
+        hashes[idx++] = nodes[c].CalculatedHashValue;
+      }
+      hashes[idx] = node.HashValue;
+      return HashUtil.JSHash(hashes);
+    }
+
+    public static HashNode<T>[] Simplify<T>(this HashNode<T>[] nodes) where T : class {
+      var reduced = nodes.Reduce();
+      reduced.Sort();
+
+      for (int i = 0; i < reduced.Length; ++i) {
+        var node = reduced[i];
+        if (node.IsChild) {
+          continue;
+        }
+        node.Simplify?.Invoke(reduced, i);
+      }
+      // detect if anything was simplified
+      var count = 0;
+      foreach (var node in reduced) {
+        if (!node.Enabled) { ++count; }
+      }
+      if (count == 0) {
+        return reduced;
+      }
+
+      var simplified = new HashNode<T>[reduced.Length - count];
+      int j = 0;
+      foreach (var node in reduced) {
+        if (node.Enabled) {
+          simplified[j++] = node;
+        }
+      }
+      simplified.UpdateNodeSizes();
+      simplified.Sort();
+      return simplified;
+    }
+
+    public static void Sort<T>(this HashNode<T>[] nodes) where T : class {
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+
+        if (node.IsChild) {
+          continue;
+        }
+
+        if (node.IsCommutative) { // only sort when the argument order does not matter
+          var arity = node.Arity;
+          var size = node.Size;
+
+          if (arity == size) { // all child nodes are terminals
+            Array.Sort(nodes, i - size, size);
+          } else { // i have some non-terminal children
+            var sorted = new HashNode<T>[size];
+            var indices = nodes.IterateChildren(i);
+            Array.Sort(indices, (a, b) => nodes[a].CompareTo(nodes[b]));
+
+            int idx = 0;
+            foreach (var j in indices) {
+              var child = nodes[j];
+              if (!child.IsChild) { // must copy complete subtree
+                Array.Copy(nodes, j - child.Size, sorted, idx, child.Size);
+                idx += child.Size;
+              }
+              sorted[idx++] = nodes[j];
+            }
+            Array.Copy(sorted, 0, nodes, i - size, size);
+          }
+        }
+        node.CalculatedHashValue = nodes.ComputeHash(i);
+      }
+    }
+
+    /// <summary>
+    /// Get a function node's child indices from left to right
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="nodes"></param>
+    /// <param name="i"></param>
+    /// <returns>An array containing child indices</returns>
+    public static int[] IterateChildren<T>(this HashNode<T>[] nodes, int i) where T : class {
+      var node = nodes[i];
+      var arity = node.Arity;
+      var children = new int[arity];
+      var idx = i - 1;
+      for (int j = 0; j < arity; ++j) {
+        while (!nodes[idx].Enabled) { --idx; } // skip nodes possibly disabled by simplification
+        children[j] = idx;
+        idx -= 1 + nodes[idx].Size;
+      }
+      return children;
+    }
+
+    public static void UpdateNodeSizes<T>(this HashNode<T>[] nodes) where T : class {
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+        if (node.IsChild) {
+          node.Size = 0;
+          continue;
+        }
+        node.Size = node.Arity;
+        foreach (int j in nodes.IterateChildren(i)) {
+          node.Size += nodes[j].Size;
+        }
+      }
+    }
+
+    /// <summary>
+    /// Reduce nodes of the same type according to arithmetic rules
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="g">The given grammar (controls symbol arities)</param>
+    /// <param name="nodes">The node array</param>
+    /// <param name="i">Parent index</param>
+    /// <param name="j">Child index</param>
+    private static void Reduce<T>(this HashNode<T>[] nodes, int i, int j) where T : class {
+      var p = nodes[i]; // parent node
+      var c = nodes[j]; // child node
+
+      if (c.IsChild)
+        return;
+
+      foreach (int k in nodes.IterateChildren(j)) {
+        if (nodes[k].IsChild) continue;
+        nodes.Reduce(j, k);
+      }
+
+      // handle commutative symbols (add, mul) 
+      if (p.IsCommutative && p.HashValue == c.HashValue) {
+        c.Enabled = false;
+        p.Arity += c.Arity - 1;
+      }
+    }
+
+    public static HashNode<T>[] Reduce<T>(this HashNode<T>[] nodes) where T : class {
+      nodes.UpdateNodeSizes();
+      int i = nodes.Length - 1;
+      foreach (int c in nodes.IterateChildren(i)) {
+        if (nodes[c].IsChild) continue;
+        nodes.Reduce(i, c);
+      }
+      int count = 0;
+      foreach (var node in nodes) {
+        if (!node.Enabled) { ++count; }
+      }
+      if (count == 0)
+        return nodes;
+
+      var reduced = new HashNode<T>[nodes.Length - count];
+      i = 0;
+      foreach (var node in nodes) {
+        if (node.Enabled) { reduced[i++] = node; }
+      }
+      reduced.UpdateNodeSizes();
+      return reduced;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs	(revision 16240)
@@ -0,0 +1,100 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Security.Cryptography;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class HashUtil {
+    // This class contains some hash functions adapted from http://partow.net/programming/hashfunctions/index.html#AvailableHashFunctions
+
+    // A simple hash function from Robert Sedgwicks Algorithms in C book.I've added some simple optimizations to the algorithm in order to speed up its hashing process. 
+    public static int RSHash(int[] input) {
+      const int b = 378551;
+      int a = 63689;
+      int hash = 0;
+
+      foreach (var v in input) {
+        hash = (hash * a) + v;
+        a *= b;
+      }
+      return hash;
+    }
+
+    // A bitwise hash function written by Justin Sobel
+    public static int JSHash(int[] input) {
+      int hash = 1315423911;
+      for (int i = 0; i < input.Length; ++i)
+        hash ^= (hash << 5) + input[i] + (hash >> 2);
+      return hash;
+    }
+
+    // This hash function comes from Brian Kernighan and Dennis Ritchie's book "The C Programming Language". It is a simple hash function using a strange set of possible seeds which all constitute a pattern of 31....31...31 etc, it seems to be very similar to the DJB hash function. 
+    public static int BKDRHash(int[] input) {
+      const int seed = 131;
+      int hash = 0;
+      foreach (var v in input) {
+        hash = (hash * seed) + v;
+      }
+      return hash;
+    }
+
+    // This is the algorithm of choice which is used in the open source SDBM project. The hash function seems to have a good over-all distribution for many different data sets. It seems to work well in situations where there is a high variance in the MSBs of the elements in a data set. 
+    public static int SDBMHash(int[] input) {
+      int hash = 0;
+      foreach (var v in input) {
+        hash = v + (hash << 6) + (hash << 16) - hash;
+      }
+      return hash;
+    }
+
+    // An algorithm produced by Professor Daniel J. Bernstein and shown first to the world on the usenet newsgroup comp.lang.c. It is one of the most efficient hash functions ever published. 
+    public static int DJBHash(int[] input) {
+      int hash = 5381;
+      foreach (var v in input) {
+        hash = (hash << 5) + hash + v;
+      }
+      return hash;
+    }
+
+    // An algorithm proposed by Donald E.Knuth in The Art Of Computer Programming Volume 3, under the topic of sorting and search chapter 6.4. 
+    public static int DEKHash(int[] input) {
+      int hash = input.Length;
+      foreach (var v in input) {
+        hash = (hash << 5) ^ (hash >> 27) ^ v;
+      }
+      return hash;
+    }
+
+    public static int CryptoHash(HashAlgorithm ha, int[] input) {
+      return BitConverter.ToInt32(ha.ComputeHash(input.ToByteArray()), 0);
+    }
+
+    public static byte[] ToByteArray(this int[] input) {
+      const int size = sizeof(int);
+      var bytes = new byte[input.Length * sizeof(int)];
+      for (int i = 0; i < input.Length; ++i) {
+        Array.Copy(BitConverter.GetBytes(bytes[i]), 0, bytes, i * size, size);
+      }
+      return bytes;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs	(revision 16240)
@@ -0,0 +1,270 @@
+﻿using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using static HeuristicLab.Problems.DataAnalysis.Symbolic.SymbolicExpressionHashExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionTreeHash {
+    private static readonly Addition add = new Addition();
+    private static readonly Subtraction sub = new Subtraction();
+    private static readonly Multiplication mul = new Multiplication();
+    private static readonly Division div = new Division();
+    private static readonly Logarithm log = new Logarithm();
+    private static readonly Exponential exp = new Exponential();
+    private static readonly Sine sin = new Sine();
+    private static readonly Cosine cos = new Cosine();
+    private static readonly Constant constant = new Constant();
+
+    private static readonly ISymbolicExpressionTreeNodeComparer comparer = new SymbolicExpressionTreeNodeComparer();
+
+    public static int ComputeHash(this ISymbolicExpressionTree tree) {
+      return ComputeHash(tree.Root.GetSubtree(0).GetSubtree(0));
+    }
+
+    public static Dictionary<ISymbolicExpressionTreeNode, int> ComputeNodeHashes(this ISymbolicExpressionTree tree) {
+      var root = tree.Root.GetSubtree(0).GetSubtree(0);
+      var nodes = root.MakeNodes();
+      nodes.UpdateNodeSizes();
+
+      for (int i = 0; i < nodes.Length; ++i) {
+        if (nodes[i].IsChild)
+          continue;
+        nodes[i].CalculatedHashValue = nodes.ComputeHash(i);
+      }
+      return nodes.ToDictionary(x => x.Data, x => x.CalculatedHashValue);
+    }
+
+    public static int ComputeHash(this ISymbolicExpressionTreeNode treeNode) {
+      var hashNodes = treeNode.MakeNodes();
+      var simplified = hashNodes.Simplify();
+      return ComputeHash(simplified);
+    }
+
+    public static int ComputeHash(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      int hash = 1315423911;
+      foreach (var node in nodes)
+        hash ^= (hash << 5) + node.CalculatedHashValue + (hash >> 2);
+      return hash;
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode> ToHashNode(this ISymbolicExpressionTreeNode node) {
+      var symbol = node.Symbol;
+      var name = symbol.Name;
+      if (symbol is Variable) {
+        var variableTreeNode = (VariableTreeNode)node;
+        name = variableTreeNode.VariableName;
+      }
+      var hash = name.GetHashCode();
+      var hashNode = new HashNode<ISymbolicExpressionTreeNode>(comparer) {
+        Data = node,
+        Arity = node.SubtreeCount,
+        Size = node.SubtreeCount,
+        IsCommutative = node.Symbol is Addition || node.Symbol is Multiplication,
+        Enabled = true,
+        HashValue = hash,
+        CalculatedHashValue = hash
+      };
+      if (symbol is Addition) {
+        hashNode.Simplify = SimplifyAddition;
+      } else if (symbol is Multiplication) {
+        hashNode.Simplify = SimplifyMultiplication;
+      } else if (symbol is Division) {
+        hashNode.Simplify = SimplifyDivision;
+      } else if (symbol is Logarithm || symbol is Exponential || symbol is Sine || symbol is Cosine) {
+        hashNode.Simplify = SimplifyUnaryNode;
+      } else if (symbol is Subtraction) {
+        hashNode.Simplify = SimplifyBinaryNode;
+      }
+      return hashNode;
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode>[] MakeNodes(this ISymbolicExpressionTreeNode node) {
+      return node.IterateNodesPostfix().Select(ToHashNode).ToArray();
+    }
+
+    #region parse a nodes array back into a tree
+    public static ISymbolicExpressionTree ToTree(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      var root = new ProgramRootSymbol().CreateTreeNode();
+      var start = new StartSymbol().CreateTreeNode();
+      root.AddSubtree(start);
+      start.AddSubtree(nodes.ToSubtree());
+      return new SymbolicExpressionTree(root);
+    }
+
+    public static ISymbolicExpressionTreeNode ToSubtree(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      var treeNodes = nodes.Select(x => x.Data.Symbol.CreateTreeNode()).ToArray();
+
+      for (int i = nodes.Length - 1; i >= 0; --i) {
+        var node = nodes[i];
+
+        if (node.IsChild) {
+          if (node.Data is VariableTreeNode variable) {
+            var variableTreeNode = (VariableTreeNode)treeNodes[i];
+            variableTreeNode.VariableName = variable.VariableName;
+            variableTreeNode.Weight = 1;
+          } else if (node.Data is ConstantTreeNode @const) {
+            var constantTreeNode = (ConstantTreeNode)treeNodes[i];
+            constantTreeNode.Value = @const.Value;
+          }
+          continue;
+        }
+
+        var treeNode = treeNodes[i];
+
+        foreach (var j in nodes.IterateChildren(i)) {
+          treeNode.AddSubtree(treeNodes[j]);
+        }
+      }
+
+      return treeNodes.Last();
+    }
+
+    private static T CreateTreeNode<T>(this ISymbol symbol) where T : class, ISymbolicExpressionTreeNode {
+      return (T)symbol.CreateTreeNode();
+    }
+    #endregion
+
+    #region tree simplification
+    // these simplification methods rely on the assumption that child nodes of the current node have already been simplified
+    // (in other words simplification should be applied in a bottom-up fashion) 
+    public static ISymbolicExpressionTree Simplify(ISymbolicExpressionTree tree) {
+      var root = tree.Root.GetSubtree(0).GetSubtree(0);
+      var nodes = root.MakeNodes();
+      var simplified = nodes.Simplify();
+      return simplified.ToTree();
+    }
+
+    public static void SimplifyAddition(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      // simplify additions of terms by eliminating terms with the same symbol and hash
+      var children = nodes.IterateChildren(i);
+
+      var curr = children[0];
+      var node = nodes[i];
+
+      foreach (var j in children.Skip(1)) {
+        if (nodes[j] == nodes[curr]) {
+          for (int k = j - nodes[j].Size; k <= j; ++k) {
+            nodes[k].Enabled = false;
+          }
+          node.Arity--;
+        } else {
+          curr = j;
+        }
+      }
+      if (node.Arity == 1) { // if the arity is 1 we don't need the addition node at all
+        node.Enabled = false;
+      }
+    }
+
+    // simplify multiplications by reducing constants and div terms  
+    public static void SimplifyMultiplication(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var node = nodes[i];
+      var children = nodes.IterateChildren(i);
+
+      for (int j = 0; j < children.Length; ++j) {
+        var c = children[j];
+        var child = nodes[c];
+
+        if (!child.Enabled)
+          continue;
+
+        var symbol = child.Data.Symbol;
+        if (symbol is Constant) {
+          for (int k = j + 1; k < children.Length; ++k) {
+            var d = children[k];
+            if (nodes[d].Data.Symbol is Constant) {
+              ((ConstantTreeNode)child.Data).Value *= ((ConstantTreeNode)nodes[d].Data).Value;
+              nodes[d].Enabled = false;
+              node.Arity--;
+            } else {
+              break;
+            }
+          }
+        } else if (symbol is Division) {
+          var div = nodes[c];
+          var denominator =
+            div.Arity == 1 ?
+            nodes[c - 1] :                    // 1 / x is expressed as div(x) (with a single child)
+            nodes[c - nodes[c - 1].Size - 2]; // assume division always has arity 1 or 2
+
+          foreach (var d in children) {
+            if (nodes[d].Enabled && nodes[d] == denominator) {
+              nodes[c].Enabled = nodes[d].Enabled = denominator.Enabled = false;
+              node.Arity -= 2; // matching child + division node
+              break;
+            }
+          }
+        }
+
+        if (node.Arity == 0) { // if everything is simplified this node becomes constant
+          var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+          constantTreeNode.Value = 1;
+          nodes[i] = constantTreeNode.ToHashNode();
+        } else if (node.Arity == 1) { // when i have only 1 arg left i can skip this node
+          node.Enabled = false;
+        }
+      }
+    }
+
+    public static void SimplifyDivision(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var node = nodes[i];
+      var children = nodes.IterateChildren(i);
+
+      if (children.All(x => nodes[x].Data.Symbol is Constant)) {
+        var v = ((ConstantTreeNode)nodes[children.First()].Data).Value;
+        if (node.Arity == 1) {
+          v = 1 / v;
+        } else if (node.Arity > 1) {
+          foreach (var j in children.Skip(1)) {
+            v /= ((ConstantTreeNode)nodes[j].Data).Value;
+          }
+        }
+        var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+        constantTreeNode.Value = v;
+        nodes[i] = constantTreeNode.ToHashNode();
+        return;
+      }
+
+      var nominator = nodes[children[0]];
+      foreach (var j in children.Skip(1)) {
+        var denominator = nodes[j];
+        if (nominator == denominator) {
+          // disable all the children of the division node (nominator and children + denominator and children)
+          nominator.Enabled = denominator.Enabled = false;
+          node.Arity -= 2; // nominator + denominator
+        }
+        if (node.Arity == 0) {
+          var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+          constantTreeNode.Value = 1; // x / x = 1
+          nodes[i] = constantTreeNode.ToHashNode();
+        }
+      }
+    }
+
+    public static void SimplifyUnaryNode(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      // check if the child of the unary node is a constant, then the whole node can be simplified
+      var parent = nodes[i];
+      var child = nodes[i - 1];
+
+      var parentSymbol = parent.Data.Symbol;
+      var childSymbol = child.Data.Symbol;
+
+      if (childSymbol is Constant) {
+        nodes[i].Enabled = false;
+      } else if ((parentSymbol is Exponential && childSymbol is Logarithm) || (parentSymbol is Logarithm && childSymbol is Exponential)) {
+        child.Enabled = parent.Enabled = false;
+      }
+    }
+
+    public static void SimplifyBinaryNode(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var children = nodes.IterateChildren(i);
+      if (children.All(x => nodes[x].Data.Symbol is Constant)) {
+        foreach (var j in children) {
+          nodes[j].Enabled = false;
+        }
+        nodes[i] = constant.CreateTreeNode().ToHashNode();
+      }
+    }
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 16240)
@@ -139,4 +139,5 @@
     <Compile Include="Converters\LinearModelToTreeConverter.cs" />
     <Compile Include="Converters\TreeSimplifier.cs" />
+    <Compile Include="Converters\DerivativeCalculator.cs" />
     <Compile Include="Converters\TreeToAutoDiffTermConverter.cs" />
     <Compile Include="Formatters\InfixExpressionFormatter.cs" />
@@ -144,4 +145,7 @@
     <Compile Include="Formatters\SymbolicDataAnalysisExpressionMathematicaFormatter.cs" />
     <Compile Include="Formatters\SymbolicDataAnalysisExpressionCSharpFormatter.cs" />
+    <Compile Include="Hashing\HashExtensions.cs" />
+    <Compile Include="Hashing\HashUtil.cs" />
+    <Compile Include="Hashing\SymbolicExpressionTreeHash.cs" />
     <Compile Include="Importer\InfixExpressionParser.cs" />
     <Compile Include="Importer\SymbolicExpressionImporter.cs" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionVariableImpactsView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionVariableImpactsView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionVariableImpactsView.cs	(revision 16240)
@@ -60,5 +60,4 @@
       InitializeComponent();
 
-      //Little workaround. If you fill the ComboBox-Items in the other partial class, the UI-Designer will moan.
       this.sortByComboBox.Items.AddRange(Enum.GetValues(typeof(SortingCriteria)).Cast<object>().ToArray());
       this.sortByComboBox.SelectedItem = SortingCriteria.ImpactValue;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs	(revision 16240)
@@ -19,4 +19,5 @@
  */
 #endregion
+
 
 namespace HeuristicLab.Problems.DataAnalysis.Views {
@@ -81,4 +82,8 @@
       this.sortByComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
       this.sortByComboBox.FormattingEnabled = true;
+      this.sortByComboBox.Items.AddRange(new object[] {
+            HeuristicLab.Problems.DataAnalysis.Views.RegressionSolutionVariableImpactsView.SortingCriteria.ImpactValue,
+            HeuristicLab.Problems.DataAnalysis.Views.RegressionSolutionVariableImpactsView.SortingCriteria.Occurrence,
+            HeuristicLab.Problems.DataAnalysis.Views.RegressionSolutionVariableImpactsView.SortingCriteria.VariableName});
       this.sortByComboBox.Location = new System.Drawing.Point(407, 3);
       this.sortByComboBox.Name = "sortByComboBox";
@@ -159,6 +164,6 @@
       // variableImactsArrayView
       // 
-      this.variableImactsArrayView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
+      this.variableImactsArrayView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+            | System.Windows.Forms.AnchorStyles.Left)
             | System.Windows.Forms.AnchorStyles.Right)));
       this.variableImactsArrayView.Caption = "StringConvertibleArray View";
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs	(revision 16240)
@@ -39,6 +39,5 @@
       VariableName
     }
-    private IProgress progress;
-    private Dictionary<string, double> rawVariableImpacts = new Dictionary<string, double>();
+    private List<Tuple<string, double>> rawVariableImpacts = new List<Tuple<string, double>>();
 
     public new IRegressionSolution Content {
@@ -53,12 +52,9 @@
       InitializeComponent();
 
-      //Little workaround. If you fill the ComboBox-Items in the other partial class, the UI-Designer will moan.
-      this.sortByComboBox.Items.AddRange(Enum.GetValues(typeof(SortingCriteria)).Cast<object>().ToArray());
-      this.sortByComboBox.SelectedItem = SortingCriteria.ImpactValue;
-
       //Set the default values
       this.dataPartitionComboBox.SelectedIndex = 0;
-      this.replacementComboBox.SelectedIndex = 0;
+      this.replacementComboBox.SelectedIndex = 3;
       this.factorVarReplComboBox.SelectedIndex = 0;
+      this.sortByComboBox.SelectedItem = SortingCriteria.ImpactValue;
     }
 
@@ -93,7 +89,5 @@
 
     private void RegressionSolutionVariableImpactsView_VisibleChanged(object sender, EventArgs e) {
-      if (!cancellationToken.IsCancellationRequested) {
-        cancellationToken.Cancel();
-      }
+      cancellationToken.Cancel();
     }
 
@@ -110,28 +104,18 @@
       //Update the default ordering (asc,desc), but remove the eventHandler beforehand (otherwise the data would be ordered twice)
       ascendingCheckBox.CheckedChanged -= ascendingCheckBox_CheckedChanged;
-      switch ((SortingCriteria)sortByComboBox.SelectedItem) {
-        case SortingCriteria.ImpactValue:
-          ascendingCheckBox.Checked = false;
-          break;
-        case SortingCriteria.Occurrence:
-          ascendingCheckBox.Checked = true;
-          break;
-        case SortingCriteria.VariableName:
-          ascendingCheckBox.Checked = true;
-          break;
-        default:
-          throw new NotImplementedException("Ordering for selected SortingCriteria not implemented");
-      }
+      ascendingCheckBox.Checked = (SortingCriteria)sortByComboBox.SelectedItem != SortingCriteria.ImpactValue;
       ascendingCheckBox.CheckedChanged += ascendingCheckBox_CheckedChanged;
 
-      UpdateDataOrdering();
+      UpdateOrdering();
     }
 
     private void ascendingCheckBox_CheckedChanged(object sender, EventArgs e) {
-      UpdateDataOrdering();
+      UpdateOrdering();
     }
 
 
     private async void UpdateVariableImpact() {
+      IProgress progress;
+
       //Check if the selection is valid
       if (Content == null) { return; }
@@ -163,9 +147,12 @@
         var problemData = Content.ProblemData;
         var inputvariables = new HashSet<string>(problemData.AllowedInputVariables.Union(Content.Model.VariablesUsedForPrediction));
-        var originalVariableOrdering = problemData.Dataset.VariableNames.Where(v => inputvariables.Contains(v)).Where(problemData.Dataset.VariableHasType<double>).ToList();
+        var originalVariableOrdering = problemData.Dataset.VariableNames
+          .Where(v => inputvariables.Contains(v))
+          .Where(v => problemData.Dataset.VariableHasType<double>(v) || problemData.Dataset.VariableHasType<string>(v))
+          .ToList();
 
         rawVariableImpacts.Clear();
-        originalVariableOrdering.ForEach(v => rawVariableImpacts.Add(v, impacts.First(vv => vv.Item1 == v).Item2));
-        UpdateDataOrdering();
+        originalVariableOrdering.ForEach(v => rawVariableImpacts.Add(new Tuple<string, double>(v, impacts.First(vv => vv.Item1 == v).Item2)));
+        UpdateOrdering();
       } finally {
         ((MainForm.WindowsForms.MainForm)MainFormManager.MainForm).RemoveOperationProgressFromView(this);
@@ -177,5 +164,5 @@
     /// The default is "Descending" by "VariableImpact" (as in previous versions)
     /// </summary>
-    private void UpdateDataOrdering() {
+    private void UpdateOrdering() {
       //Check if valid sortingCriteria is selected and data exists
       if (sortByComboBox.SelectedIndex == -1) { return; }
@@ -186,10 +173,10 @@
       bool ascending = ascendingCheckBox.Checked;
 
-      IEnumerable<KeyValuePair<string, double>> orderedEntries = null;
+      IEnumerable<Tuple<string, double>> orderedEntries = null;
 
       //Sort accordingly
       switch (selectedItem) {
         case SortingCriteria.ImpactValue:
-          orderedEntries = rawVariableImpacts.OrderBy(v => v.Value);
+          orderedEntries = rawVariableImpacts.OrderBy(v => v.Item2);
           break;
         case SortingCriteria.Occurrence:
@@ -197,5 +184,5 @@
           break;
         case SortingCriteria.VariableName:
-          orderedEntries = rawVariableImpacts.OrderBy(v => v.Key, new NaturalStringComparer());
+          orderedEntries = rawVariableImpacts.OrderBy(v => v.Item1, new NaturalStringComparer());
           break;
         default:
@@ -206,6 +193,6 @@
 
       //Write the data back
-      var impactArray = new DoubleArray(orderedEntries.Select(i => i.Value).ToArray()) {
-        ElementNames = orderedEntries.Select(i => i.Key)
+      var impactArray = new DoubleArray(orderedEntries.Select(i => i.Item2).ToArray()) {
+        ElementNames = orderedEntries.Select(i => i.Item1)
       };
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs	(revision 16240)
@@ -116,17 +116,5 @@
 
     public ModifiableDataset ToModifiable() {
-      var values = new List<IList>();
-      foreach (var v in variableNames) {
-        if (VariableHasType<double>(v)) {
-          values.Add(new List<double>((IList<double>)variableValues[v]));
-        } else if (VariableHasType<string>(v)) {
-          values.Add(new List<string>((IList<string>)variableValues[v]));
-        } else if (VariableHasType<DateTime>(v)) {
-          values.Add(new List<DateTime>((IList<DateTime>)variableValues[v]));
-        } else {
-          throw new ArgumentException("Unknown variable type.");
-        }
-      }
-      return new ModifiableDataset(variableNames, values);
+      return new ModifiableDataset(variableNames, variableNames.Select(v => variableValues[v]), true);
     }
 
@@ -141,5 +129,5 @@
     }
 
-    protected Dataset(Dataset dataset) : this(dataset.variableNames, dataset.variableValues.Values) { }
+
 
     #region Backwards compatible code, remove with 3.5
@@ -318,7 +306,8 @@
     #region IStringConvertibleMatrix Members
     [Storable]
-    protected int rows;
+    private int rows;
     public int Rows {
       get { return rows; }
+      protected set { rows = value; }
     }
     int IStringConvertibleMatrix.Rows {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 16240)
@@ -163,5 +163,5 @@
 
       var variables = dataset.VariableNames.Where(variable => dataset.VariableHasType<double>(variable) || dataset.VariableHasType<string>(variable));
-      var inputVariables = new CheckedItemList<StringValue>(variables.Select(x => new StringValue(x)));
+      var inputVariables = new CheckedItemList<StringValue>(variables.Select(x => new StringValue(x).AsReadOnly()));
       foreach (StringValue x in inputVariables)
         inputVariables.SetItemCheckedState(x, allowedInputVariables.Contains(x.Value));
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs	(revision 16240)
@@ -47,6 +47,34 @@
     public ModifiableDataset() { }
 
-    public ModifiableDataset(IEnumerable<string> variableNames, IEnumerable<IList> variableValues) :
-      base(variableNames, variableValues, cloneValues: false) { }
+    public ModifiableDataset(IEnumerable<string> variableNames, IEnumerable<IList> variableValues, bool cloneValues = false) :
+      base(variableNames, variableValues, cloneValues) { }
+
+    public Dataset ToDataset() {
+      return new Dataset(variableNames, variableNames.Select(v => variableValues[v]));
+    }
+
+
+    public IEnumerable<object> GetRow(int row) {
+      return variableValues.Select(x => x.Value[row]);
+    }
+
+    public void AddRow(IEnumerable<object> values) {
+      var list = values.ToList();
+      if (list.Count != variableNames.Count)
+        throw new ArgumentException("The number of values must be equal to the number of variable names.");
+      // check if all the values are of the correct type
+      for (int i = 0; i < list.Count; ++i) {
+        if (list[i].GetType() != GetVariableType(variableNames[i])) {
+          throw new ArgumentException("The type of the provided value does not match the variable type.");
+        }
+      }
+      // add values
+      for (int i = 0; i < list.Count; ++i) {
+        variableValues[variableNames[i]].Add(list[i]);
+      }
+      Rows++;
+      OnRowsChanged();
+      OnReset();
+    }
 
     public void ReplaceRow(int row, IEnumerable<object> values) {
@@ -64,4 +92,42 @@
         variableValues[variableNames[i]][row] = list[i];
       }
+      OnReset();
+    }
+
+    // slow, avoid using this
+    public void RemoveRow(int row) {
+      foreach (var list in variableValues.Values)
+        list.RemoveAt(row);
+      Rows--;
+      OnRowsChanged();
+      OnReset();
+    }
+
+    // adds a new variable to the dataset
+    public void AddVariable(string variableName, IList values) {
+      InsertVariable(variableName, Columns, values);
+    }
+
+    public void InsertVariable(string variableName, int position, IList values) {
+      if (variableValues.ContainsKey(variableName))
+        throw new ArgumentException(string.Format("Variable {0} is already present in the dataset.", variableName));
+
+      if (position < 0 || position > Columns)
+        throw new ArgumentException(string.Format("Incorrect position {0} specified. The position must be between 0 and {1}.", position, Columns));
+
+      if (values == null)
+        throw new ArgumentNullException("values", "Values must not be null. At least an empty list of values has to be provided.");
+
+      if (values.Count != Rows)
+        throw new ArgumentException(string.Format("{0} values are provided, but {1} rows are present in the dataset.", values.Count, Rows));
+
+      if (!IsAllowedType(values))
+        throw new ArgumentException(string.Format("Unsupported type {0} for variable {1}.", GetElementType(values), variableName));
+
+      variableNames.Insert(position, variableName);
+      variableValues[variableName] = values;
+
+      OnColumnsChanged();
+      OnColumnNamesChanged();
       OnReset();
     }
@@ -77,41 +143,4 @@
     }
 
-    public void AddRow(IEnumerable<object> values) {
-      var list = values.ToList();
-      if (list.Count != variableNames.Count)
-        throw new ArgumentException("The number of values must be equal to the number of variable names.");
-      // check if all the values are of the correct type
-      for (int i = 0; i < list.Count; ++i) {
-        if (list[i].GetType() != GetVariableType(variableNames[i])) {
-          throw new ArgumentException("The type of the provided value does not match the variable type.");
-        }
-      }
-      // add values
-      for (int i = 0; i < list.Count; ++i) {
-        variableValues[variableNames[i]].Add(list[i]);
-      }
-      rows++;
-      OnRowsChanged();
-      OnReset();
-    }
-
-    // adds a new variable to the dataset
-    public void AddVariable(string variableName, IList values) {
-      if (variableValues.ContainsKey(variableName))
-        throw new ArgumentException(string.Format("Variable {0} is already present in the dataset.", variableName));
-
-      if (values == null || values.Count == 0)
-        throw new ArgumentException("Cannot add variable with no values.");
-
-      if (!IsAllowedType(values))
-        throw new ArgumentException(string.Format("Unsupported type {0} for variable {1}.", GetElementType(values), variableName));
-
-      variableValues[variableName] = values;
-      variableNames.Add(variableName);
-
-      OnColumnsChanged();
-      OnColumnNamesChanged();
-      OnReset();
-    }
 
     public void RemoveVariable(string variableName) {
@@ -125,12 +154,13 @@
     }
 
-    // slow, avoid using this
-    public void RemoveRow(int row) {
-      foreach (var list in variableValues.Values)
-        list.RemoveAt(row);
-      rows--;
+    public void ClearValues() {
+      foreach (var list in variableValues.Values) {
+        list.Clear();
+      }
+      Rows = 0;
       OnRowsChanged();
       OnReset();
     }
+
 
     public void SetVariableValue(object value, string variableName, int row) {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis.Views/3.3/DataAnalysisImportDialog.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis.Views/3.3/DataAnalysisImportDialog.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis.Views/3.3/DataAnalysisImportDialog.cs	(revision 16240)
@@ -33,5 +33,5 @@
   public partial class DataAnalysisImportDialog : Form {
 
-    private static readonly List<KeyValuePair<DateTimeFormatInfo, string>> dateTimeFormats =
+    private static readonly List<KeyValuePair<DateTimeFormatInfo, string>> POSSIBLE_DATETIME_FORMATS =
       new List<KeyValuePair<DateTimeFormatInfo, string>>{
         new KeyValuePair<DateTimeFormatInfo, string>(DateTimeFormatInfo.GetInstance(new CultureInfo("de-DE")), "dd/mm/yyyy hh:MM:ss" ),
@@ -42,7 +42,7 @@
 
     private static readonly List<KeyValuePair<char, string>> POSSIBLE_SEPARATORS =
-      new List<KeyValuePair<char, string>>{  
+      new List<KeyValuePair<char, string>>{
         new KeyValuePair<char, string>(';', "; (Semicolon)" ),
-        new KeyValuePair<char, string>(',', ", (Comma)" ),    
+        new KeyValuePair<char, string>(',', ", (Comma)" ),
         new KeyValuePair<char, string>('\t', "\\t (Tab)"),
         new KeyValuePair<char, string>((char)0, "all whitespaces (including tabs and spaces)")
@@ -52,5 +52,5 @@
       new List<KeyValuePair<NumberFormatInfo, string>>{
         new KeyValuePair<NumberFormatInfo, string>(NumberFormatInfo.GetInstance(new CultureInfo("de-DE")), ", (Comma)"),
-        new KeyValuePair<NumberFormatInfo, string>(NumberFormatInfo.InvariantInfo, ". (Period)" )    
+        new KeyValuePair<NumberFormatInfo, string>(NumberFormatInfo.InvariantInfo, ". (Period)" )
     };
 
@@ -59,6 +59,6 @@
         new KeyValuePair<Encoding, string>(Encoding.Default, "Default"),
         new KeyValuePair<Encoding, string>(Encoding.ASCII, "ASCII"),
-        new KeyValuePair<Encoding, string>(Encoding.Unicode, "Unicode"),    
-        new KeyValuePair<Encoding, string>(Encoding.UTF8, "UTF8")        
+        new KeyValuePair<Encoding, string>(Encoding.Unicode, "Unicode"),
+        new KeyValuePair<Encoding, string>(Encoding.UTF8, "UTF8")
       };
 
@@ -97,5 +97,5 @@
       DecimalSeparatorComboBox.ValueMember = "Key";
       DecimalSeparatorComboBox.DisplayMember = "Value";
-      DateTimeFormatComboBox.DataSource = dateTimeFormats;
+      DateTimeFormatComboBox.DataSource = POSSIBLE_DATETIME_FORMATS;
       DateTimeFormatComboBox.ValueMember = "Key";
       DateTimeFormatComboBox.DisplayMember = "Value";
@@ -104,4 +104,13 @@
       EncodingComboBox.DisplayMember = "Value";
 
+
+      // set default values based on the current culture
+      var separator = POSSIBLE_SEPARATORS.Where(n => n.Value.Substring(0, 1) == CultureInfo.CurrentCulture.TextInfo.ListSeparator);
+      if (separator.Any())
+        SeparatorComboBox.SelectedItem = separator.First();
+
+      var decimalSeparator = POSSIBLE_DECIMAL_SEPARATORS.Where(n => n.Value.Substring(0,1) == CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);
+      if (decimalSeparator.Any())
+        DecimalSeparatorComboBox.SelectedItem = decimalSeparator.First();
     }
 
@@ -158,5 +167,5 @@
         OkButton.Enabled = true;
       }
-      catch (Exception ex) {
+       catch (Exception ex) {
         if (ex is IOException || ex is InvalidOperationException || ex is ArgumentException) {
           OkButton.Enabled = false;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/HeuristicLab.Problems.Instances.DataAnalysis-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/HeuristicLab.Problems.Instances.DataAnalysis-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/HeuristicLab.Problems.Instances.DataAnalysis-3.3.csproj	(revision 16240)
@@ -167,4 +167,6 @@
     <Compile Include="Regression\MibaFriction\CF1.cs" />
     <Compile Include="Regression\MibaFriction\MibaFrictionRegressionInstanceProvider.cs" />
+    <Compile Include="Regression\PennML\PennMLRegressionDataDescriptor.cs" />
+    <Compile Include="Regression\PennML\PennMLRegressionInstanceProvider.cs" />
     <Compile Include="Regression\VariableNetworks\LinearVariableNetwork.cs" />
     <Compile Include="Regression\VariableNetworks\GaussianProcessVariableNetwork.cs" />
@@ -257,4 +259,5 @@
     <EmbeddedResource Include="Regression\Data\RegressionRealWorld.zip" />
     <EmbeddedResource Include="Regression\Data\MibaFriction.zip" />
+    <EmbeddedResource Include="Regression\Data\PennML.zip" />
   </ItemGroup>
   <ItemGroup>
@@ -308,4 +311,5 @@
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF1.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF1.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF1.cs	(revision 16240)
@@ -22,6 +22,6 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class CF1 : ResourceRegressionDataDescriptor {
-    public CF1(string resourceName) : base(resourceName) { }
-    public override string Name { get { return "CF1"; } }
+    public CF1() : base("CF1.csv") { }
+    public override string Name { get { return "CF1"; } }    
     public override string Description {
       get {
@@ -34,13 +34,17 @@
     protected override string TargetVariable { get { return "Cf1"; } }
     protected override string[] VariableNames {
-      get { return new string[] { "Partition", "Source1", "Source2", "x1", "Material_Cat", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "Material", "Grooving", "Oil", "x17", "x18", "x19", "x20", "x21", "x22", "Cf1"
- }; }
+      get {
+        return new string[] { "Partition", "Source1", "Source2", "x1", "Material_Cat", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "Material", "Grooving", "Oil", "x17", "x18", "x19", "x20", "x21", "x22", "Cf1"
+ };
+      }
     }
 
     protected override string[] AllowedInputVariables {
-      get { return new string[] { "Material_Cat",
+      get {
+        return new string[] { "Material_Cat",
         "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16",
         "Material", "Grooving", "Oil",
-        "x17", "x20", "x22" }; }
+        "x17", "x20", "x22" };
+      }
     }
     protected override int TrainingPartitionStart { get { return 0; } }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF2.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF2.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF2.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class CF2 : ResourceRegressionDataDescriptor {
-    public CF2(string resourceName) : base(resourceName) { }
+    public CF2() : base("CF2.csv") { }
     public override string Name { get { return "CF2"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF3.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF3.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF3.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class CF3 : ResourceRegressionDataDescriptor {
-    public CF3(string resourceName) : base(resourceName) { }
+    public CF3() : base("CF3.csv") { }
     public override string Name { get { return "CF3"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF4.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF4.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/CF4.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class CF4 : ResourceRegressionDataDescriptor {
-    public CF4(string resourceName) : base(resourceName) { }
+    public CF4() : base("CF4.csv") { }
     public override string Name { get { return "CF4"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/MibaFrictionRegressionInstanceProvider.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/MibaFrictionRegressionInstanceProvider.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/MibaFrictionRegressionInstanceProvider.cs	(revision 16240)
@@ -47,13 +47,13 @@
     public override IEnumerable<IDataDescriptor> GetDataDescriptors() {
       List<ResourceRegressionDataDescriptor> descriptorList = new List<ResourceRegressionDataDescriptor>();
-      descriptorList.Add(new CF1("CF1.csv"));
-      descriptorList.Add(new CF2("CF2.csv"));
-      descriptorList.Add(new CF3("CF3.csv"));
-      descriptorList.Add(new CF4("CF4.csv"));
-      descriptorList.Add(new NvhRating("NvhRating.csv"));
-      descriptorList.Add(new Temp1("Temp1.csv"));
-      descriptorList.Add(new Temp2("Temp2.csv"));
-      descriptorList.Add(new Wear1("Wear1.csv"));
-      descriptorList.Add(new Wear2("Wear2.csv"));
+      descriptorList.Add(new CF1());
+      descriptorList.Add(new CF2());
+      descriptorList.Add(new CF3());
+      descriptorList.Add(new CF4());
+      descriptorList.Add(new NvhRating());
+      descriptorList.Add(new Temp1());
+      descriptorList.Add(new Temp2());
+      descriptorList.Add(new Wear1());
+      descriptorList.Add(new Wear2());
 
       return descriptorList;
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/NvhRating.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/NvhRating.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/NvhRating.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class NvhRating : ResourceRegressionDataDescriptor {
-    public NvhRating(string resourceName) : base(resourceName) { }
+    public NvhRating() : base("NvhRating.csv") { }
     public override string Name { get { return "NvhRating"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Temp1.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Temp1.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Temp1.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Temp1 : ResourceRegressionDataDescriptor {
-    public Temp1(string resourceName) : base(resourceName) { }
+    public Temp1() : base("Temp1.csv") { }
     public override string Name { get { return "Temp1"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Temp2.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Temp2.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Temp2.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Temp2 : ResourceRegressionDataDescriptor {
-    public Temp2(string resourceName) : base(resourceName) { }
+    public Temp2() : base("Temp2.csv") { }
     public override string Name { get { return "Temp2"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Wear1.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Wear1.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Wear1.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Wear1 : ResourceRegressionDataDescriptor {
-    public Wear1(string resourceName) : base(resourceName) { }
+    public Wear1() : base("Wear1.csv") { }
     public override string Name { get { return "Wear1"; } }
     public override string Description {
@@ -33,4 +33,5 @@
     }
     protected override string TargetVariable { get { return "Wear1"; } }
+
     protected override string[] VariableNames {
       get { return new string[] { "Partition", "Source1", "Source2", "x1", "Material_Cat", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "Material", "Grooving", "Oil", "x17", "x18", "x19", "x20", "x21", "x22", "Wear1"
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Wear2.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Wear2.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/MibaFriction/Wear2.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Wear2 : ResourceRegressionDataDescriptor {
-    public Wear2(string resourceName) : base(resourceName) { }
+    public Wear2() : base("Wear2.csv") { }
     public override string Name { get { return "Wear2"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/PennML/PennMLRegressionDataDescriptor.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/PennML/PennMLRegressionDataDescriptor.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/PennML/PennMLRegressionDataDescriptor.cs	(revision 16240)
@@ -0,0 +1,86 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Data;
+
+namespace HeuristicLab.Problems.Instances.DataAnalysis {
+  public class PennMLRegressionDataDescriptor : ResourceRegressionDataDescriptor {
+    private readonly string[] variableNames;
+    private readonly string[] allowedInputVariables;
+    private readonly string target;
+    private readonly int trainStart;
+    private readonly int trainEnd;
+    private readonly int testStart;
+    private readonly int testEnd;
+
+    public PennMLRegressionDataDescriptor(string resourceName) : base(resourceName) { }
+
+    public PennMLRegressionDataDescriptor(string resourceName, IEnumerable<string> variableNames, IEnumerable<string> allowedInputVariables, string target,
+      IntRange trainRange, IntRange testRange) : this(resourceName) {
+      this.variableNames = variableNames.ToArray();
+      this.allowedInputVariables = allowedInputVariables.ToArray();
+      this.target = target;
+
+      this.trainStart = trainRange.Start;
+      this.trainEnd = trainRange.End;
+      this.testStart = testRange.Start;
+      this.testEnd = testRange.End;
+    }
+
+    public override string Name {
+      get { return ResourceName.Replace(".csv", ""); }
+    }
+
+    public override string Description {
+      get { return "No description available."; }
+    }
+
+    protected override string TargetVariable {
+      get { return target; }
+    }
+
+    protected override string[] VariableNames {
+      get { return variableNames; }
+    }
+
+    protected override string[] AllowedInputVariables {
+      get { return allowedInputVariables; }
+    }
+
+    protected override int TrainingPartitionStart {
+      get { return trainStart; }
+    }
+
+    protected override int TrainingPartitionEnd {
+      get { return trainEnd; }
+    }
+
+    protected override int TestPartitionStart {
+      get { return testStart; }
+    }
+
+    protected override int TestPartitionEnd {
+      get { return testEnd; }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/PennML/PennMLRegressionInstanceProvider.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/PennML/PennMLRegressionInstanceProvider.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/PennML/PennMLRegressionInstanceProvider.cs	(revision 16240)
@@ -0,0 +1,91 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using HeuristicLab.Data;
+
+namespace HeuristicLab.Problems.Instances.DataAnalysis {
+  public class PennMLRegressionInstanceProvider : ResourceRegressionInstanceProvider {
+    public override string Name {
+      get { return "PennML Regression Problems"; }
+    }
+
+    public override string Description {
+      get { return "A set of datasets used for benchmarking symbolic regression algorithms."; }
+    }
+
+    public override Uri WebLink {
+      get { return new Uri("https://github.com/EpistasisLab/penn-ml-benchmarks"); }
+    }
+
+    public override string ReferencePublication {
+      get { return "Patryk Orzechowski, William La Cava, Jason H. Moore - Where are we now? A large benchmark study of recent symbolic regression methods"; }
+    }
+
+    protected override string FileName {
+      get { return "PennML"; }
+    }
+
+    // the reference publication uses 75% of the samples in each of the datasets for training and the remaining 25% for testing 
+    private const double trainTestSplit = 0.75;
+
+    public override IEnumerable<IDataDescriptor> GetDataDescriptors() {
+      var instanceArchiveName = GetResourceName(FileName + @"\.zip");
+      using (var instancesZipFile = new ZipArchive(GetType().Assembly.GetManifestResourceStream(instanceArchiveName), ZipArchiveMode.Read)) {
+        foreach (var entry in instancesZipFile.Entries) {
+          NumberFormatInfo numberFormat;
+          DateTimeFormatInfo dateFormat;
+          char separator;
+          using (var stream = entry.Open()) {
+            // the method below disposes the stream 
+            TableFileParser.DetermineFileFormat(stream, out numberFormat, out dateFormat, out separator);
+          }
+
+          using (var stream = entry.Open()) {
+            using (var reader = new StreamReader(stream)) {
+              var header = reader.ReadLine(); // read the first line
+
+              // by convention each dataset from the PennML collection reserves the last column for the target
+              var variableNames = header.Split(separator);
+              var allowedInputVariables = variableNames.Take(variableNames.Length - 1);
+              var target = variableNames.Last();
+
+              // count lines
+              int lines = 0; while (reader.ReadLine() != null) lines++;
+
+              var trainEnd = (int)Math.Round(lines * trainTestSplit);
+              var trainRange = new IntRange(0, trainEnd);
+              var testRange = new IntRange(trainEnd, lines);
+
+              var descriptor = new PennMLRegressionDataDescriptor(entry.Name, variableNames, allowedInputVariables, target, trainRange, testRange);
+              yield return descriptor;
+            }
+          }
+        }
+      }
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/ChemicalOne.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/ChemicalOne.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/ChemicalOne.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class ChemicalOne : ResourceRegressionDataDescriptor {
-    public ChemicalOne(string resourceName) : base(resourceName) { }
+    public ChemicalOne() : base("Chemical-I.csv") { }
     public override string Name { get { return "Chemical-I"; } }
     public override string Description { get { return ""; } }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Housing.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Housing.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Housing.cs	(revision 16240)
@@ -22,5 +22,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Housing : ResourceRegressionDataDescriptor {
-    public Housing(string resourceName) : base(resourceName) { }
+    public Housing() : base("Housing.csv") { }
     public override string Name { get { return "Housing"; } }
     public override string Description { get { return ""; } }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Powermeter.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Powermeter.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Powermeter.cs	(revision 16240)
@@ -24,5 +24,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Powermeter : ResourceRegressionDataDescriptor {
-    public Powermeter(string resourceName) : base(resourceName) { }
+    public Powermeter() : base("Powermeter.txt") { }
     public override string Name { get { return "Powermeter"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/RealWorldRegressionInstanceProvider.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/RealWorldRegressionInstanceProvider.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/RealWorldRegressionInstanceProvider.cs	(revision 16240)
@@ -47,9 +47,9 @@
     public override IEnumerable<IDataDescriptor> GetDataDescriptors() {
       List<ResourceRegressionDataDescriptor> descriptorList = new List<ResourceRegressionDataDescriptor>();
-      descriptorList.Add(new ChemicalOne("Chemical-I.csv"));
-      descriptorList.Add(new Housing("Housing.csv"));
-      descriptorList.Add(new Tower("Tower.txt"));
-      descriptorList.Add(new Powermeter("Powermeter.txt"));
-      descriptorList.Add(new SARCOS("SARCOS - Inverse Dynamics.txt"));
+      descriptorList.Add(new ChemicalOne());
+      descriptorList.Add(new Housing());
+      descriptorList.Add(new Tower());
+      descriptorList.Add(new Powermeter());
+      descriptorList.Add(new SARCOS());
       return descriptorList;
     }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/SARCOS.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/SARCOS.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/SARCOS.cs	(revision 16240)
@@ -24,5 +24,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class SARCOS : ResourceRegressionDataDescriptor {
-    public SARCOS(string resourceName) : base(resourceName) { }
+    public SARCOS() : base("SARCOS - Inverse Dynamics.txt") { }
     public override string Name { get { return "SARCOS - Inverse Dynamics"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Tower.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Tower.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/RealWorld/Tower.cs	(revision 16240)
@@ -24,5 +24,5 @@
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
   public class Tower : ResourceRegressionDataDescriptor {
-    public Tower(string resourceName) : base(resourceName) { }
+    public Tower() : base("Tower.txt") { }
     public override string Name { get { return "Tower"; } }
     public override string Description {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/HeuristicLab.Random-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/HeuristicLab.Random-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/HeuristicLab.Random-3.3.csproj	(revision 16240)
@@ -120,4 +120,5 @@
     <Compile Include="GammaDistributedRandom.cs" />
     <Compile Include="ListExtensions.cs" />
+    <Compile Include="RandomSeedGenerator.cs" />
     <Compile Include="UniformDistributedRandom.cs" />
     <Compile Include="FastRandom.cs" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/RandomCreator.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/RandomCreator.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/RandomCreator.cs	(revision 16240)
@@ -94,5 +94,5 @@
       IntValue seed = SeedParameter.ActualValue;
 
-      if (setSeedRandomly) seed.Value = new System.Random().Next();
+      if (setSeedRandomly) seed.Value = RandomSeedGenerator.GetSeed();
       IRandom random = (IRandom)RandomType.Clone();
       random.Reset(seed.Value);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/RandomSeedGenerator.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/RandomSeedGenerator.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Random/3.3/RandomSeedGenerator.cs	(revision 16240)
@@ -0,0 +1,31 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+namespace HeuristicLab.Random {
+  public static class RandomSeedGenerator {
+    private static readonly System.Random rand = new System.Random();
+    private static readonly object locker = new object();
+
+    public static int GetSeed() {
+      lock (locker) return rand.Next();
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/ASPNETAuthentication.dbml
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/ASPNETAuthentication.dbml	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/ASPNETAuthentication.dbml	(revision 16240)
@@ -1,4 +1,4 @@
 ﻿<?xml version="1.0" encoding="utf-8"?><Database Name="HeuristicLab.Authentication" Class="ASPNETAuthenticationDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
-  <Connection Mode="AppSettings" ConnectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" SettingsObjectName="HeuristicLab.Services.Access.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_AuthenticationConnectionString" Provider="System.Data.SqlClient" />
+  <Connection Mode="AppSettings" ConnectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" SettingsObjectName="HeuristicLab.Services.Access.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_AuthenticationConnectionString" Provider="System.Data.SqlClient" />
   <Table Name="dbo.aspnet_Applications" Member="aspnet_Applications">
     <Type Name="aspnet_Application">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/AccessService.dbml
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/AccessService.dbml	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/AccessService.dbml	(revision 16240)
@@ -1,4 +1,4 @@
 ﻿<?xml version="1.0" encoding="utf-8"?><Database Name="HeuristicLab.ClientManagement" Class="AccessServiceDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
-  <Connection Mode="AppSettings" ConnectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService" SettingsObjectName="HeuristicLab.Services.Access.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_ClientManagementConnectionString" Provider="System.Data.SqlClient" />
+  <Connection Mode="AppSettings" ConnectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService" SettingsObjectName="HeuristicLab.Services.Access.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_ClientManagementConnectionString" Provider="System.Data.SqlClient" />
   <Table Name="dbo.Resource" Member="Resources">
     <Type Name="Resource" InheritanceCode="Resource" IsInheritanceDefault="true">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/Properties/Settings.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 16240)
@@ -13,5 +13,5 @@
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
     public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
         
@@ -27,6 +27,6 @@
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
         [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
-        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=.\\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Ac" +
-            "cessService")]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Acces" +
+            "sService")]
         public string HeuristicLab_ClientManagementConnectionString {
             get {
@@ -38,6 +38,6 @@
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
         [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
-        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=.\\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Au" +
-            "thentication")]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authe" +
+            "ntication")]
         public string HeuristicLab_AuthenticationConnectionString {
             get {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/Properties/Settings.settings
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/Properties/Settings.settings	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/Properties/Settings.settings	(revision 16240)
@@ -6,16 +6,14 @@
       <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
 &lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
-  &lt;ConnectionString&gt;Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService&lt;/ConnectionString&gt;
-  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
+  &lt;ConnectionString&gt;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService&lt;/ConnectionString&gt;
 &lt;/SerializableConnectionString&gt;</DesignTimeValue>
-      <Value Profile="(Default)">Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService</Value>
+      <Value Profile="(Default)">Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService</Value>
     </Setting>
     <Setting Name="HeuristicLab_AuthenticationConnectionString" Type="(Connection string)" Scope="Application">
       <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
 &lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
-  &lt;ConnectionString&gt;Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication&lt;/ConnectionString&gt;
-  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
+  &lt;ConnectionString&gt;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication&lt;/ConnectionString&gt;
 &lt;/SerializableConnectionString&gt;</DesignTimeValue>
-      <Value Profile="(Default)">Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication</Value>
+      <Value Profile="(Default)">Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication</Value>
     </Setting>
   </Settings>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access.DataAccess/3.3/app.config	(revision 16240)
@@ -1,7 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
+  <configSections>
+  </configSections>
   <connectionStrings>
-    <add name="HeuristicLab.Services.Access.DataAccess.Properties.Settings.HeuristicLab_ClientManagementConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService" />
-    <add name="HeuristicLab.Services.Access.DataAccess.Properties.Settings.HeuristicLab_AuthenticationConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Services.Access.DataAccess.Properties.Settings.HeuristicLab_ClientManagementConnectionString"
+      connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.AccessService" />
+    <add name="HeuristicLab.Services.Access.DataAccess.Properties.Settings.HeuristicLab_AuthenticationConnectionString"
+      connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
   </connectionStrings>
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Interfaces/IRoleVerifier.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Interfaces/IRoleVerifier.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Interfaces/IRoleVerifier.cs	(revision 16240)
@@ -23,4 +23,5 @@
   public interface IRoleVerifier {
     bool IsInRole(string role);
+    bool IsUserInRole(string username, string role);
     bool IsInAnyRole(params string[] roles);
     bool IsInAllRoles(params string[] roles);
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Interfaces/IUserManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Interfaces/IUserManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Interfaces/IUserManager.cs	(revision 16240)
@@ -31,5 +31,7 @@
     MembershipUser GetUserById(Guid userId);
     string GetUserNameById(Guid userId);
+    IEnumerable<Guid> GetUserGroupIdsOfUser(Guid userId);
     bool VerifyUser(Guid userId, List<Guid> allowedUserGroups);
+    IEnumerable<DataTransfer.UserGroupMapping> GetUserGroupMapping();
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/RoleVerifier.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/RoleVerifier.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/RoleVerifier.cs	(revision 16240)
@@ -29,4 +29,7 @@
       return Roles.IsUserInRole(role);
     }
+    public bool IsUserInRole(string username, string role) {
+      return Roles.IsUserInRole(username, role);
+    }
     public bool IsInAnyRole(params string[] roles) {
       return roles.Any(x => Roles.IsUserInRole(x));
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/UserManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/UserManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/UserManager.cs	(revision 16240)
@@ -46,5 +46,5 @@
     public string GetUserNameById(Guid userId) {
       var user = GetUserById(userId);
-      if (user != null) {
+      if (user != null && !string.IsNullOrWhiteSpace(user.UserName)) {
         return user.UserName;
       } else {
@@ -52,4 +52,18 @@
       }
 
+    }
+
+    public IEnumerable<Guid> GetUserGroupIdsOfUser(Guid userId) {
+      using (DA.AccessServiceDataContext context = new DA.AccessServiceDataContext()) {
+        var groupIds = from g in context.UserGroupUserGroups
+                       where g.UserGroupId == userId
+                       select g.UserGroupUserGroupId;
+
+        var query = from g in context.UserGroupBases.OfType<DA.UserGroup>()
+                    where groupIds.Contains(g.Id)
+                    select g.Id;
+
+        return query.ToList();
+      }
     }
 
@@ -74,4 +88,12 @@
       }
       return false;
+    }
+
+    public IEnumerable<DataTransfer.UserGroupMapping> GetUserGroupMapping() {
+      using (DA.AccessServiceDataContext context = new DA.AccessServiceDataContext()) {
+        var query = from u in context.UserGroupUserGroups
+                    select Convert.ToDto(u);
+        return query.ToList();
+      }
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Web.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Web.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/Web.config	(revision 16240)
@@ -2,5 +2,5 @@
 <configuration>
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
   </connectionStrings>
 
@@ -30,5 +30,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/AccessService-3.3/AccessService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/AccessService-3.3/AccessService.svc" />
           </baseAddresses>
         </host>
@@ -41,5 +41,5 @@
           <serviceDebug includeExceptionDetailInFaults="true" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Access/3.3/app.config	(revision 16240)
@@ -2,5 +2,5 @@
 <configuration>
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
   </connectionStrings>
 
@@ -30,5 +30,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/AccessService-3.3/AccessService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/AccessService-3.3/AccessService.svc" />
           </baseAddresses>
         </host>
@@ -41,5 +41,5 @@
           <serviceDebug includeExceptionDetailInFaults="true" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Deployment.dbml
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Deployment.dbml	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Deployment.dbml	(revision 16240)
@@ -1,4 +1,4 @@
 ﻿<?xml version="1.0" encoding="utf-8"?><Database Name="HeuristicLab.PluginStore" Class="DeploymentDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
-  <Connection Mode="AppSettings" ConnectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment" SettingsObjectName="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_DeploymentConnectionString" Provider="System.Data.SqlClient" />
+  <Connection Mode="AppSettings" ConnectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment" SettingsObjectName="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_DeploymentConnectionString" Provider="System.Data.SqlClient" />
   <Table Name="dbo.Dependencies" Member="Dependencies">
     <Type Name="Dependency">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 16240)
@@ -13,5 +13,5 @@
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
     internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
         
@@ -27,6 +27,6 @@
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
         [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
-        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=.\\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.De" +
-            "ployment")]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deplo" +
+            "yment")]
         public string HeuristicLab_DeploymentConnectionString {
             get {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.settings
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.settings	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.settings	(revision 16240)
@@ -6,8 +6,7 @@
       <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
 &lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
-  &lt;ConnectionString&gt;Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment&lt;/ConnectionString&gt;
-  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
+  &lt;ConnectionString&gt;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment&lt;/ConnectionString&gt;
 &lt;/SerializableConnectionString&gt;</DesignTimeValue>
-      <Value Profile="(Default)">Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment</Value>
+      <Value Profile="(Default)">Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment</Value>
     </Setting>
   </Settings>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment.DataAccess/3.3/app.config	(revision 16240)
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
+  <configSections>
+  </configSections>
   <connectionStrings>
-    <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_DeploymentConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment" />
+    <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_DeploymentConnectionString"
+      connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment" />
   </connectionStrings>
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment/3.3/Web.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment/3.3/Web.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment/3.3/Web.config	(revision 16240)
@@ -2,6 +2,6 @@
 <configuration>
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
-    <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_DeploymentConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment"/>
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_DeploymentConnectionString" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment"/>
   </connectionStrings>
 
@@ -40,5 +40,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/Deployment-3.3/AdminService.svc"/>
+            <add baseAddress="http://services.heuristiclab.com/Deployment-3.3/AdminService.svc"/>
           </baseAddresses>
         </host>
@@ -49,5 +49,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/Deployment-3.3/UpdateService.svc"/>
+            <add baseAddress="http://services.heuristiclab.com/Deployment-3.3/UpdateService.svc"/>
           </baseAddresses>
         </host>
@@ -60,5 +60,5 @@
           <serviceDebug includeExceptionDetailInFaults="true" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Deployment/3.3/app.config	(revision 16240)
@@ -2,6 +2,6 @@
 <configuration>
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
-    <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_DeploymentConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment"/>
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_DeploymentConnectionString" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Deployment"/>
   </connectionStrings>
 
@@ -40,5 +40,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/Deployment-3.3/AdminService.svc"/>
+            <add baseAddress="http://services.heuristiclab.com/Deployment-3.3/AdminService.svc"/>
           </baseAddresses>
         </host>
@@ -49,5 +49,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/Deployment-3.3/UpdateService.svc"/>
+            <add baseAddress="http://services.heuristiclab.com/Deployment-3.3/UpdateService.svc"/>
           </baseAddresses>
         </host>
@@ -60,5 +60,5 @@
           <serviceDebug includeExceptionDetailInFaults="true" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/AssignedJobResourceDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/AssignedJobResourceDao.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/AssignedJobResourceDao.cs	(revision 16240)
@@ -0,0 +1,248 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Data.Linq;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HeuristicLab.Services.Hive.DataAccess.Daos {
+  public class AssignedJobResourceDao : GenericDao<Guid, AssignedJobResource> {
+    public AssignedJobResourceDao(DataContext dataContext) : base(dataContext) { }
+
+    public override AssignedJobResource GetById(Guid id) {
+      throw new NotImplementedException();
+    }
+
+    public IQueryable<AssignedJobResource> GetByJobId(Guid jobId) {
+      return Table.Where(x => x.JobId == jobId);
+    }
+
+    public void DeleteByProjectId(Guid projectId) {
+      DataContext.ExecuteCommand(DeleteByProjectIdQueryString, projectId);
+    }
+
+    public void DeleteByProjectIdAndUserIds(Guid projectId, IEnumerable<Guid> userIds) {
+      string paramUserIds = string.Join(",", userIds.Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramUserIds)) {
+        string queryString = string.Format(DeleteByProjectIdAndUserIdsQueryString, projectId, paramUserIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public void DeleteByProjectIds(IEnumerable<Guid> projectIds) {
+      string paramProjectIds = string.Join(",", projectIds.Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds)) {
+        string queryString = string.Format(DeleteByProjectIdsQueryString, paramProjectIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public void DeleteByProjectIdsAndUserIds(IEnumerable<Guid> projectIds, IEnumerable<Guid> userIds) {
+      string paramProjectIds = string.Join(",", projectIds.Select(x => string.Format("'{0}'", x)));
+      string paramUserIds = string.Join(",", userIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds) && !string.IsNullOrWhiteSpace(paramUserIds)) {
+        string queryString = string.Format(DeleteByProjectIdsAndUserIdsQueryString, paramProjectIds, paramUserIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public void DeleteByProjectIdAndResourceIds(Guid projectId, IEnumerable<Guid> resourceIds) {
+      string paramResourceIds = string.Join(",", resourceIds.Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string queryString = string.Format(DeleteByProjectIdAndResourceIdsQueryString, projectId, paramResourceIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public void DeleteByProjectIdsAndResourceIds(IEnumerable<Guid> projectIds, IEnumerable<Guid> resourceIds) {
+      string paramProjectIds = string.Join(",", projectIds.Select(x => string.Format("'{0}'", x)));
+      string paramResourceIds = string.Join(",", resourceIds.Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds) && !string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string queryString = string.Format(DeleteByProjectIdsAndResourceIdsQueryString, paramProjectIds, paramResourceIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public bool CheckJobGrantedForResource(Guid jobId, Guid resourceId) {
+      return DataContext.ExecuteQuery<int>(CheckJobGrantedForResourceQueryString, jobId, resourceId).First() > 0;
+    }
+
+    public bool CheckJobGrantedForResources(Guid jobId, IEnumerable<Guid> resourceIds) {
+      string paramResourceIds = string.Join(",", resourceIds.Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string queryString = string.Format(CheckJobGrantedForResourcesQueryString, jobId, paramResourceIds);
+        return DataContext.ExecuteQuery<int>(queryString).Count() == 0;
+      }
+      return false;
+    }
+
+    public bool CheckTaskGrantedForResource(Guid taskId, Guid resourceId) {
+      return DataContext.ExecuteQuery<int>(CheckTaskGrantedForResourceQueryString, taskId, resourceId).First() > 0;
+    }
+
+    public IEnumerable<Resource> GetAllGrantedResourcesByJobId(Guid jobId) {
+      return DataContext.ExecuteQuery<Resource>(GetAllGrantedResourcesByJobIdQueryString, jobId);
+    }
+
+    public IEnumerable<Guid> GetAllGrantedResourceIdsByJobId(Guid jobId) {
+      return DataContext.ExecuteQuery<Guid>(GetAllGrantedResourceIdsByJobIdQueryString, jobId);
+    }
+
+    #region String queries
+    private const string DeleteByProjectIdQueryString = @"
+      DELETE FROM [AssignedJobResource]
+      WHERE JobId IN
+	      (
+		      SELECT j.JobId
+		      FROM [Job] j
+		      WHERE j.ProjectId = {0}
+	      )
+    ";
+    private const string DeleteByProjectIdAndUserIdsQueryString = @"
+      DELETE FROM [AssignedJobResource]
+      WHERE JobId IN
+	      (
+		      SELECT j.JobId
+		      FROM [Job] j
+		      WHERE j.ProjectId = '{0}'
+          AND j.OwnerUserId IN ({1})
+	      )
+    ";
+    private const string DeleteByProjectIdsQueryString = @"
+      DELETE FROM [AssignedJobResource]
+      WHERE JobId IN
+	      (
+		      SELECT j.JobId
+		      FROM [Job] j
+		      WHERE j.ProjectId IN ({0})
+	      )
+    ";
+    private const string DeleteByProjectIdsAndUserIdsQueryString = @"
+      DELETE FROM [AssignedJobResource]
+      WHERE JobId IN
+	      (
+		      SELECT j.JobId
+		      FROM [Job] j
+		      WHERE j.ProjectId IN ({0})
+          AND j.OwnerUserId IN ({1})
+	      )
+    ";
+    private const string DeleteByProjectIdAndResourceIdsQueryString = @"
+      DELETE FROM [AssignedJobResource]
+      WHERE JobId IN
+	      (
+		      SELECT j.JobId
+		      FROM [Job] j
+		      WHERE j.ProjectId = '{0}'
+	      )
+      AND ResourceId IN ({1})
+    ";
+    private const string DeleteByProjectIdsAndResourceIdsQueryString = @"
+      DELETE FROM [AssignedJobResource]
+      WHERE JobId IN
+	      (
+		      SELECT j.JobId
+		      FROM [Job] j
+		      WHERE j.ProjectId IN ({0})
+	      )
+      AND ResourceId IN ({1})
+    ";
+    private const string CheckJobGrantedForResourceQueryString = @"
+      WITH rbranch AS (
+        SELECT ResourceId, ParentResourceId
+        FROM [Resource]
+        WHERE ResourceId = {1}
+        UNION ALL
+        SELECT r.ResourceId, r.ParentResourceId
+        FROM [Resource] r
+        JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId
+      )
+      SELECT COUNT(ajr.JobId)
+      FROM rbranch, [AssignedJobResource] ajr
+      WHERE rbranch.ResourceId = ajr.ResourceId
+      AND ajr.JobId = {0}
+    ";
+    private const string CheckTaskGrantedForResourceQueryString = @"
+      WITH rbranch AS (
+        SELECT ResourceId, ParentResourceId
+        FROM [Resource]
+        WHERE ResourceId = {1}
+        UNION ALL
+        SELECT r.ResourceId, r.ParentResourceId
+        FROM [Resource] r
+        JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId
+      )
+      SELECT COUNT(ajr.JobId)
+      FROM rbranch, [AssignedJobResource] ajr, [Task] t
+      WHERE rbranch.ResourceId = ajr.ResourceId
+      AND ajr.JobId = t.JobId
+      AND t.JobId = {0}
+    ";
+    private const string CheckJobGrantedForResourcesQueryString = @"
+      WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+      )
+      SELECT r.ResourceId
+      FROM [Resource] r
+      WHERE r.ResourceId IN ({1})
+      EXCEPT
+      (
+	      SELECT rtree.ResourceId
+	      FROM rtree, [AssignedJobResource] ajr
+	      WHERE rtree.ParentResourceId = ajr.ResourceId
+	      AND ajr.JobId = {0}
+	      UNION
+	      SELECT ajr.ResourceId
+	      FROM [AssignedJobResource] ajr
+	      WHERE ajr.JobId = {0}
+      )
+    ";
+    private const string GetAllGrantedResourcesByJobIdQueryString = @"
+      WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+      )
+      SELECT res.*
+      FROM rtree, [AssignedJobResource] ajr, [Resource] res
+      WHERE rtree.ParentResourceId = ajr.ResourceId
+      AND rtree.ResourceId = res.ResourceId
+      AND ajr.JobId = {0}
+      UNION
+      SELECT res.*
+      FROM [AssignedJobResource] ajr, [Resource] res
+      WHERE ajr.ResourceId = res.ResourceId
+      AND ajr.JobId = {0}
+    ";
+    private const string GetAllGrantedResourceIdsByJobIdQueryString = @"
+    WITH rtree AS
+    (
+	    SELECT ResourceId, ParentResourceId
+	    FROM [Resource]
+	    UNION ALL
+	    SELECT rt.ResourceId, r.ParentResourceId
+	    FROM [Resource] r
+	    JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+    )
+    SELECT rtree.ResourceId
+    FROM rtree, [AssignedJobResource] ajr
+    WHERE rtree.ParentResourceId = ajr.ResourceId
+    AND ajr.JobId = {0}
+    UNION
+    SELECT ajr.ResourceId
+    FROM [AssignedJobResource] ajr
+    WHERE ajr.JobId = {0}
+    ";
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/AssignedProjectResourceDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/AssignedProjectResourceDao.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/AssignedProjectResourceDao.cs	(revision 16240)
@@ -0,0 +1,233 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Data.Linq;
+using System.Linq;
+
+namespace HeuristicLab.Services.Hive.DataAccess.Daos {
+  public class AssignedProjectResourceDao : GenericDao<Guid, AssignedProjectResource> {
+    public AssignedProjectResourceDao(DataContext dataContext) : base(dataContext) { }
+
+    public override AssignedProjectResource GetById(Guid id) {
+      throw new NotImplementedException();
+    }
+
+    public IQueryable<AssignedProjectResource> GetByProjectId(Guid projectId) {
+      return Table.Where(x => x.ProjectId == projectId);
+    }
+
+    public void DeleteByProjectIdAndResourceIds(Guid projectId, IEnumerable<Guid> resourceIds) {
+      string paramIds = string.Join(",", resourceIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramIds)) {
+        string query = string.Format(DeleteByProjectIdAndResourceIdsQueryString, projectId, paramIds);
+        DataContext.ExecuteCommand(query);
+      }
+    }
+
+    public void DeleteByProjectIdsAndResourceIds(IEnumerable<Guid> projectIds, IEnumerable<Guid> resourceIds) {
+      string paramProjectIds = string.Join(",", projectIds.ToList().Select(x => string.Format("'{0}'", x)));
+      string paramResourceIds = string.Join(",", resourceIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds) && !string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string query = string.Format(DeleteByProjectIdsAndResourceIdsQueryString, paramProjectIds, paramResourceIds);
+        DataContext.ExecuteCommand(query);
+      }
+    }
+
+    public void DeleteByProjectIds(IEnumerable<Guid> projectIds) {
+      string paramProjectIds = string.Join(",", projectIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds)) {
+        string query = string.Format(DeleteByProjectIdsQueryString, paramProjectIds);
+        DataContext.ExecuteCommand(query);
+      }
+    }
+
+    public bool CheckProjectGrantedForResources(Guid projectId, IEnumerable<Guid> resourceIds) {
+      string paramResourceIds = string.Join(",", resourceIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string queryString = string.Format(CheckProjectGrantedForResourcesQueryString, projectId, paramResourceIds);
+        return DataContext.ExecuteQuery<int>(queryString).Count() == 0;
+      }
+      return false;
+    }
+
+    public IEnumerable<Resource> GetAllGrantedResourcesByProjectId(Guid projectId) {
+      return DataContext.ExecuteQuery<Resource>(GetAllGrantedResourcesByProjectIdQueryString, projectId);
+    }
+
+    public IEnumerable<Guid> GetAllGrantedResourceIdsByProjectId(Guid projectId) {
+      return DataContext.ExecuteQuery<Guid>(GetAllGrantedResourceIdsByProjectIdQueryString, projectId);
+    }
+
+    public IEnumerable<Resource> GetAllGrantedResourcesByProjectIds(IEnumerable<Guid> projectIds) {
+      string paramProjectIds = string.Join(",", projectIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds)) {
+        string queryString = string.Format(GetAllGrantedResourcesByProjectIdsQueryString, paramProjectIds);
+        return DataContext.ExecuteQuery<Resource>(queryString);
+      }
+      return Enumerable.Empty<Resource>();
+    }
+
+    public IEnumerable<Guid> GetAllGrantedResourceIdsOfOwnedParentProjects(Guid projectId, Guid userId) {
+      return DataContext.ExecuteQuery<Guid>(GetAllGrantedResourceIdsOfOwnedParentProjectsQueryString, projectId, userId);
+    }
+
+
+    #region Compiled queries
+    private static readonly Func<DataContext, Guid, IEnumerable<AssignedProjectResource>> GetByProjectIdGetByIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid projectId) =>
+        from projectPermission in db.GetTable<AssignedProjectResource>()
+        where projectPermission.ProjectId == projectId
+        select projectPermission);
+    #endregion
+
+    #region String queries
+    private const string DeleteByProjectIdAndResourceIdsQueryString = @"
+      DELETE FROM [AssignedProjectResource]
+        WHERE ProjectId = '{0}'
+        AND ResourceId IN ({1});
+    ";
+    private const string DeleteByProjectIdsAndResourceIdsQueryString = @"
+      DELETE FROM [AssignedProjectResource]
+        WHERE ProjectId IN ({0})
+        AND ResourceId IN ({1});
+    ";
+    private const string DeleteByProjectIdsQueryString = @"
+      DELETE FROM [AssignedProjectResource]
+        WHERE ProjectId IN ({0})
+    ";
+    private const string CheckProjectGrantedForResourcesQueryString = @"
+    WITH rtree AS
+    (
+	    SELECT ResourceId, ParentResourceId
+	    FROM [Resource]
+	    UNION ALL
+	    SELECT rt.ResourceId, r.ParentResourceId
+	    FROM [Resource] r
+	    JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+    )
+    SELECT r.ResourceId
+    FROM [Resource] r
+    WHERE r.ResourceId IN ({1})
+    EXCEPT
+    (
+	    SELECT rtree.ResourceId
+	    FROM rtree, [AssignedProjectResource] apr
+	    WHERE rtree.ParentResourceId = apr.ResourceId
+	    AND apr.ProjectId = '{0}'
+	    UNION
+	    SELECT apr.ResourceId
+	    FROM [AssignedProjectResource] apr
+	    WHERE apr.ProjectId = '{0}'
+    )
+    ";
+    private const string GetAllGrantedResourcesByProjectIdQueryString = @"
+      WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+      )
+      SELECT res.*
+      FROM rtree, [AssignedProjectResource] apr, [Resource] res
+      WHERE rtree.ParentResourceId = apr.ResourceId
+      AND rtree.ResourceId = res.ResourceId
+      AND apr.ProjectId = '{0}'
+      UNION
+      SELECT res.*
+      FROM [AssignedProjectResource] apr, [Resource] res
+      WHERE apr.ResourceId = res.ResourceId
+      AND apr.ProjectId = '{0}'
+    ";
+    private const string GetAllGrantedResourceIdsByProjectIdQueryString = @"
+    WITH rtree AS
+    (
+	    SELECT ResourceId, ParentResourceId
+	    FROM [Resource]
+	    UNION ALL
+	    SELECT rt.ResourceId, r.ParentResourceId
+	    FROM [Resource] r
+	    JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+    )
+    SELECT rtree.ResourceId
+    FROM rtree, [AssignedProjectResource] apr
+    WHERE rtree.ParentResourceId = apr.ResourceId
+    AND apr.ProjectId = '{0}'
+    UNION
+    SELECT apr.ResourceId
+    FROM [AssignedProjectResource] apr
+    WHERE apr.ProjectId = '{0}'
+    ";
+    private const string GetAllGrantedResourcesByProjectIdsQueryString = @"
+      WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+      )
+      SELECT res.*
+      FROM rtree, [AssignedProjectResource] apr, [Resource] res
+      WHERE rtree.ParentResourceId = apr.ResourceId
+      AND rtree.ResourceId = res.ResourceId
+      AND apr.ProjectId IN ({0})
+      UNION
+      SELECT res.*
+      FROM [AssignedProjectResource] apr, [Resource] res
+      WHERE apr.ResourceId = res.ResourceId
+      AND apr.ProjectId IN ({0})
+    ";
+    private const string GetAllGrantedResourceIdsOfOwnedParentProjectsQueryString = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId
+	      FROM [Project]
+	      UNION ALL
+	      SELECT pb.ProjectId, p.ParentProjectId
+	      FROM [Project] p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pb.ParentProjectId <> pb.ProjectId
+      ),
+      rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId AND r.ParentResourceId <> r.ResourceId AND rt.ParentResourceId <> rt.ResourceId
+      )
+      SELECT DISTINCT rtree.ResourceId
+      FROM pbranch, rtree, [Project] pro, [AssignedProjectResource] apr
+      WHERE pbranch.ProjectId = {0}
+      AND pbranch.ParentProjectId = pro.ProjectId
+      AND pro.OwnerUserId = {1}
+      AND pbranch.ParentProjectId = apr.ProjectId
+      AND apr.ResourceId = rtree.ParentResourceId
+    ";
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimClientDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimClientDao.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimClientDao.cs	(revision 16240)
@@ -33,10 +33,26 @@
     }
 
-    public IQueryable<DimClient> GetActiveClients() {
-      return Table.Where(x => x.ExpirationTime == null);
+    public IQueryable<DimClient> GetAllOnlineClients() {
+      return Table.Where(x => x.DateExpired == null);
     }
 
-    public IQueryable<DimClient> GetExpiredClients() {
-      return Table.Where(x => x.ExpirationTime != null);
+    public IQueryable<DimClient> GetAllExpiredClients() {
+      return Table.Where(x => x.DateExpired != null);
+    }
+
+    public IQueryable<DimClient> GetAllOnlineSlaves() {
+      return Table.Where(x => x.DateExpired == null && x.ResourceType == "Slave");
+    }
+
+    public IQueryable<DimClient> GetAllOnlineSlaveGroups() {
+      return Table.Where(x => x.DateExpired == null && x.ResourceType == "GROUP");
+    }
+
+    public IQueryable<DimClient> GetAllExpiredSlaves() {
+      return Table.Where(x => x.DateExpired != null && x.ResourceType == "Slave");
+    }
+
+    public IQueryable<DimClient> GetAllExpiredSlaveGroups() {
+      return Table.Where(x => x.DateExpired != null && x.ResourceType == "GROUP");
     }
 
@@ -44,5 +60,5 @@
       string paramIds = string.Join(",", ids.Select(x => string.Format("'{0}'", x)));
       if (!string.IsNullOrWhiteSpace(paramIds)) {
-        string query = string.Format(UpdateExpirationTimeQuery, "{0}", paramIds);
+        string query = string.Format(UpdateDateExpiredQuery, "{0}", paramIds);
         return DataContext.ExecuteCommand(query, time);
       }
@@ -59,7 +75,7 @@
 
     #region String queries
-    private const string UpdateExpirationTimeQuery =
+    private const string UpdateDateExpiredQuery =
       @"UPDATE [statistics].[DimClient] 
-           SET ExpirationTime = {0} 
+           SET DateExpired = {0} 
          WHERE Id IN ({1});";
     #endregion
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimProjectDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimProjectDao.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimProjectDao.cs	(revision 16240)
@@ -0,0 +1,72 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Data.Linq;
+using System.Linq;
+
+namespace HeuristicLab.Services.Hive.DataAccess.Daos.HiveStatistics {
+  public class DimProjectDao : GenericDao<Guid, DimProject> {
+    public DimProjectDao(DataContext dataContext) : base(dataContext) { }
+
+    public override DimProject GetById(Guid id) {
+      return GetByIdQuery(DataContext, id);
+    }
+
+    public IEnumerable<DimProject> GetByProjectId(Guid projectId) {
+      return GetByProjectIdQuery(DataContext, projectId);
+    }
+
+    public Guid GetLastValidIdByProjectId(Guid projectId) {
+      return GetLastValidIdByProjectIdQuery(DataContext, projectId);
+    }
+
+    public IEnumerable<DimProject> GetAllOnlineProjects() {
+      return GetAllOnlineProjectsQuery(DataContext);
+    }
+
+    #region Compiled queries
+    private static readonly Func<DataContext, Guid, DimProject> GetByIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid id) =>
+        (from dimProject in db.GetTable<DimProject>()
+         where dimProject.Id == id
+         select dimProject).SingleOrDefault());
+    private static readonly Func<DataContext, Guid, IEnumerable<DimProject>> GetByProjectIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid projectId) =>
+        (from dimProject in db.GetTable<DimProject>()
+         where dimProject.ProjectId == projectId
+         select dimProject));
+    private static readonly Func<DataContext, Guid, Guid> GetLastValidIdByProjectIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid projectId) =>
+        (from dimProject in db.GetTable<DimProject>()
+         where dimProject.ProjectId == projectId
+         && dimProject.DateExpired == null
+         orderby dimProject.DateCreated descending
+         select dimProject.Id).SingleOrDefault());
+    private static readonly Func<DataContext, IEnumerable<DimProject>> GetAllOnlineProjectsQuery =
+      CompiledQuery.Compile((DataContext db) =>
+        (from dimProject in db.GetTable<DimProject>()
+         where dimProject.DateExpired == null
+         select dimProject));
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/FactProjectInfoDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/FactProjectInfoDao.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/FactProjectInfoDao.cs	(revision 16240)
@@ -0,0 +1,46 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Data.Linq;
+using System.Linq;
+
+namespace HeuristicLab.Services.Hive.DataAccess.Daos.HiveStatistics {
+  public class FactProjectInfoDao : GenericDao<Guid, FactProjectInfo> {
+    public FactProjectInfoDao(DataContext dataContext) : base(dataContext) { }
+
+    public override FactProjectInfo GetById(Guid id) {
+      return GetByIdQuery(DataContext, id);
+    }
+
+    private Table<DimTime> DimTimeTable {
+      get { return DataContext.GetTable<DimTime>(); }
+    }
+
+    #region Compiled queries
+    private static readonly Func<DataContext, Guid, FactProjectInfo> GetByIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid id) =>
+        (from factProjectInfo in db.GetTable<FactProjectInfo>()
+         where factProjectInfo.ProjectId== id
+         select factProjectInfo).SingleOrDefault());
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/FactTaskDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/FactTaskDao.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/FactTaskDao.cs	(revision 16240)
@@ -66,5 +66,5 @@
       return from factTask in Table
              join client in DimClientTable on factTask.LastClientId equals client.Id
-             where client.ResourceGroupId == id
+             where client.ParentResourceId == id
              select factTask;
     }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/JobDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/JobDao.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/JobDao.cs	(revision 16240)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections.Generic;
 using System.Data.Linq;
 using System.Linq;
@@ -32,4 +33,34 @@
     }
 
+    public void DeleteByState(JobState state) {
+      DataContext.ExecuteCommand(DeleteByStateQueryString, Enum.GetName(typeof(JobState), state));
+    }
+
+    public IEnumerable<Job> GetByProjectId(Guid id) {
+      return GetByProjectIdQuery(DataContext, id);
+    }
+
+    public IEnumerable<Job> GetByProjectIds(IEnumerable<Guid> projectIds) {
+      string paramProjectIds = string.Join(",", projectIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if(!string.IsNullOrWhiteSpace(paramProjectIds)) {
+        string queryString = string.Format(GetByProjectIdsQueryString, paramProjectIds);
+        return DataContext.ExecuteQuery<Job>(queryString);
+      }
+      return Enumerable.Empty<Job>();
+    }
+
+    public IEnumerable<Job> GetByState(JobState state) {
+      return GetByStateQuery(DataContext, state);
+    }
+
+    public IEnumerable<Guid> GetJobIdsByState(JobState state) {
+      return GetJobIdsByStateQuery(DataContext, state);
+    }
+
+    public IEnumerable<Job> GetJobsReadyForDeletion() {
+      return GetJobsReadyForDeletionQuery(DataContext);
+    }
+
+
     #region Compiled queries
     private static readonly Func<DataContext, Guid, Job> GetByIdQuery =
@@ -38,4 +69,50 @@
          where job.JobId == jobId
          select job).SingleOrDefault());
+    private static readonly Func<DataContext, Guid, IEnumerable<Job>> GetByProjectIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid projectId) =>
+        (from job in db.GetTable<Job>()
+         where job.ProjectId == projectId
+         select job));
+    private static readonly Func<DataContext, JobState, IEnumerable<Job>> GetByStateQuery =
+      CompiledQuery.Compile((DataContext db, JobState jobState) =>
+        (from job in db.GetTable<Job>()
+         where job.State == jobState
+         select job));
+    private static readonly Func<DataContext, JobState, IEnumerable<Guid>> GetJobIdsByStateQuery =
+      CompiledQuery.Compile((DataContext db, JobState jobState) =>
+        (from job in db.GetTable<Job>()
+         where job.State == jobState
+         select job.JobId));
+    private static readonly Func<DataContext, IEnumerable<Job>> GetJobsReadyForDeletionQuery =
+      CompiledQuery.Compile((DataContext db) =>
+        (from job in db.GetTable<Job>()
+         where job.State == JobState.StatisticsPending
+         && (from task in db.GetTable<Task>()
+             where task.JobId == job.JobId
+             select task.State == TaskState.Finished
+              || task.State == TaskState.Aborted
+              || task.State == TaskState.Failed).All(x => x)
+         select job));
+    #endregion
+
+    #region String queries
+    private const string DeleteByStateQueryString = @"
+      DELETE FROM [Job]
+      WHERE JobState = {0}
+    ";
+    private const string GetStatisticsPendingJobs = @"
+      SELECT DISTINCT j.*
+      FROM [Job] j
+      WHERE j.JobState = 'StatisticsPending'
+      AND 'Calculating' NOT IN (
+        SELECT t.TaskState 
+        FROM [Task] t 
+        WHERE t.JobId = j.JobId)
+    ";
+    private const string GetByProjectIdsQueryString = @"
+      SELECT DISTINCT j.*
+      FROM [Job] j
+      WHERE j.ProjectId IN ({0})
+    ";
     #endregion
   }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ProjectDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ProjectDao.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ProjectDao.cs	(revision 16240)
@@ -0,0 +1,293 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Data.Linq;
+using System.Linq;
+
+namespace HeuristicLab.Services.Hive.DataAccess.Daos {
+  public class ProjectDao : GenericDao<Guid, Project> {
+    public ProjectDao(DataContext dataContext) : base(dataContext) { }
+
+    public class ProjectStats {
+      public Guid ProjectId { get; set; }
+      public int Cores { get; set; }
+      public int Memory { get; set; }
+    }
+
+    public override Project GetById(Guid id) {
+      return GetByIdQuery(DataContext, id);
+    }
+
+    public void DeleteByIds(IEnumerable<Guid> ids) {
+      string paramProjectIds = string.Join(",", ids.ToList().Select(x => string.Format("'{0}'", x)));
+      if(!string.IsNullOrWhiteSpace(paramProjectIds)) {
+        string queryString = string.Format(DeleteByIdsQueryString, paramProjectIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public IEnumerable<Project> GetUsageGrantedProjectsForUser(IEnumerable<Guid> userAndGroupIds) {
+      string paramUserAndGroupIds = string.Join(",", userAndGroupIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramUserAndGroupIds)) {
+        string queryString = string.Format(GetUsageGrantedProjectsForUserQueryString, paramUserAndGroupIds);
+        return DataContext.ExecuteQuery<Project>(queryString);
+      }
+      return Enumerable.Empty<Project>();
+    }
+
+    public IEnumerable<Project> GetAdministrationGrantedProjectsForUser(Guid userId) {
+      return DataContext.ExecuteQuery<Project>(GetAdministrationGrantedProjectsForUserQueryString, userId);
+    }
+
+    public IEnumerable<Project> GetChildProjectsById(Guid id) {
+      return DataContext.ExecuteQuery<Project>(GetChildProjectsByIdQuery, id);
+    }
+
+    public IEnumerable<Guid> GetChildProjectIdsById(Guid id) {
+      return DataContext.ExecuteQuery<Guid>(GetChildProjectIdsByIdQuery, id);
+    }
+
+    public IEnumerable<Project> GetParentProjectsById(Guid id) {
+      return DataContext.ExecuteQuery<Project>(GetParentProjectsByIdQuery, id);
+    }
+
+    public IEnumerable<Guid> GetParentProjectIdsById(Guid id) {
+      return DataContext.ExecuteQuery<Guid>(GetParentProjectIdsByIdQuery, id);
+    }
+
+    public IEnumerable<Project> GetCurrentAndParentProjectsById(Guid id) {
+      return DataContext.ExecuteQuery<Project>(GetCurrentAndParentProjectsByIdQuery, id);
+    }
+
+    public IEnumerable<Guid> GetCurrentAndParentProjectIdsById(Guid id) {
+      return DataContext.ExecuteQuery<Guid>(GetCurrentAndParentProjectIdsByIdQuery, id);
+    }
+
+    public IEnumerable<ProjectStats> GetUsageStatsPerProject() {
+      return DataContext.ExecuteQuery<ProjectStats>(GetUsageStatsPerProjectQueryString).ToList();
+    }
+
+    public IEnumerable<ProjectStats> GetAvailabilityStatsPerProject() {
+      return DataContext.ExecuteQuery<ProjectStats>(GetAvailabilityStatsPerProjectQueryString).ToList();
+    }
+
+    #region Compiled queries
+    private static readonly Func<DataContext, Guid, Project> GetByIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid projectId) =>
+        (from project in db.GetTable<Project>()
+         where project.ProjectId == projectId
+         select project).SingleOrDefault());
+    #endregion
+
+    #region String queries
+    private const string DeleteByIdsQueryString = @"
+      DELETE FROM [Project]
+      WHERE ProjectId IN ({0})
+    ";
+
+    private const string GetUsageStatsPerProjectQueryString = @"
+      SELECT j.ProjectId, SUM(t.CoresNeeded) AS Cores, SUM(t.MemoryNeeded) AS Memory
+      FROM [Task] t, [Job] j
+      WHERE t.TaskState = 'Calculating'
+      AND t.JobId = j.JobId
+      GROUP BY j.ProjectId
+    ";
+
+    private const string GetAvailabilityStatsPerProjectQueryString = @"
+      WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId
+      )
+      SELECT apr.ProjectId, SUM(res.Cores) AS Cores, SUM(res.Memory) AS Memory
+      FROM rtree, [AssignedProjectResource] apr, [Resource] res
+      WHERE rtree.ResourceId = res.ResourceId
+      AND res.ResourceType = 'Slave'
+      AND (res.SlaveState = 'Idle' OR SlaveState = 'Calculating')
+      AND rtree.ParentResourceId = apr.ResourceId
+      GROUP BY apr.ProjectId
+      UNION
+      SELECT apr.ProjectId, SUM(res.Cores) AS Cores, SUM(res.Memory) AS Memory
+      FROM [AssignedProjectResource] apr, [Resource] res
+      WHERE apr.ResourceId = res.ResourceId
+      AND res.ResourceType = 'Slave'
+      AND (res.SlaveState = 'Idle' OR SlaveState = 'Calculating')
+      GROUP BY apr.ProjectId
+    ";
+
+    private const string GetUsageGrantedProjectsForUserQueryString = @"
+      SELECT DISTINCT p.*
+      FROM [Project] p, [ProjectPermission] pp
+      WHERE p.ProjectId = pp.ProjectId
+      AND pp.GrantedUserId IN ({0})
+    ";
+
+    private const string GetAdministrationGrantedProjectsForUserQueryString = @"
+      WITH ptree AS
+      (
+	      SELECT ProjectId, ParentProjectId
+	      FROM [Project]
+	      UNION ALL
+	      SELECT pt.ProjectId, p.ParentProjectId
+	      FROM [Project] p
+	      JOIN ptree pt ON pt.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pt.ParentProjectId <> pt.ProjectId
+      )
+      SELECT DISTINCT parent.*
+      FROM [Project] parent
+      WHERE parent.OwnerUserId = {0}
+      UNION
+      SELECT DISTINCT child.* 
+      FROM ptree, [Project] parent, [Project] child
+      WHERE ptree.ParentProjectId = parent.ProjectId
+      AND ptree.ProjectId = child.ProjectId
+      AND parent.OwnerUserId = {0}
+    ";
+
+    private const string GetChildProjectsByIdQuery = @"
+    	WITH ptree AS
+      (
+	      SELECT ProjectId, ParentProjectId
+	      FROM [Project]
+	      UNION ALL
+	      SELECT pt.ProjectId, p.ParentProjectId
+	      FROM [Project] p
+	      JOIN ptree pt ON pt.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pt.ParentProjectId <> pt.ProjectId
+      )
+      SELECT DISTINCT pro.*
+      FROM ptree, [Project] pro
+      WHERE ptree.ParentProjectId = {0}
+	    AND ptree.ProjectId = pro.ProjectId
+    ";
+    private const string GetChildProjectIdsByIdQuery = @"
+      WITH ptree AS
+      (
+	      SELECT ProjectId, ParentProjectId
+	      FROM [Project]
+	      UNION ALL
+	      SELECT pt.ProjectId, r.ParentProjectId
+	      FROM [Project] r
+	      JOIN ptree pt ON pt.ParentProjectId = r.ProjectId AND r.ParentProjectId <> r.ProjectId AND pt.ParentProjectId <> pt.ProjectId
+      )
+      SELECT DISTINCT ptree.ProjectId
+      FROM ptree
+      WHERE ptree.ParentProjectId = {0}
+    ";
+    private const string GetParentProjectsByIdQuery = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId
+        FROM [Project]
+	      UNION ALL
+	      SELECT pb.ProjectId, p.ParentProjectId
+	      FROM [Project] p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pb.ParentProjectId <> pb.ProjectId
+      )
+      SELECT DISTINCT pro.*
+      FROM pbranch, [Project] pro
+      WHERE pbranch.ProjectId = {0}
+      AND pbranch.ParentProjectId = pro.ProjectId
+    ";
+    private const string GetParentProjectIdsByIdQuery = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId
+        FROM [Project]
+	      UNION ALL
+	      SELECT pb.ProjectId, p.ParentProjectId
+	      FROM [Project] p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pb.ParentProjectId <> pb.ProjectId
+      )
+      SELECT DISTINCT pbranch.ParentProjectId
+      FROM pbranch
+      WHERE pbranch.ProjectId = {0}
+    ";
+    private const string GetCurrentAndParentProjectsByIdQuery = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId
+	      FROM Project
+	      WHERE ProjectId = {0}
+	      UNION ALL
+	      SELECT p.ProjectId, p.ParentProjectId
+	      FROM Project p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId
+      )
+      SELECT DISTINCT pro.*
+      FROM pbranch, Project pro
+      WHERE pbranch.ProjectId = pro.ProjectId
+    ";
+    private const string GetCurrentAndParentProjectIdsByIdQuery = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId
+	      FROM Project
+	      WHERE ProjectId = {0}
+	      UNION ALL
+	      SELECT p.ProjectId, p.ParentProjectId
+	      FROM Project p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId
+      )
+      SELECT DISTINCT pbranch.ProjectId
+      FROM pbranch
+    ";
+    private const string GetNearestOwnedParentProjectByIdQuery = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId, CAST(ProjectId AS NVARCHAR(MAX)) Path, 1 Distance
+	      FROM [Project]
+	      WHERE ProjectId = {0}
+	      UNION ALL
+	      SELECT pb.ProjectId, p.ParentProjectId, pb.Path + N', ' + CAST(pb.ProjectId AS NVARCHAR(MAX)), pb.Distance + 1
+	      FROM [Project] p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pb.ParentProjectId <> pb.ProjectId
+      )
+      SELECT TOP(1) pro.*
+      FROM pbranch, [Project] pro
+      WHERE pbranch.ParentProjectId = pro.ProjectId
+      AND pro.OwnerUserId = {1}
+      ORDER BY pbranch.Distance
+    ";
+    private const string GetFarestOwnedParentProjectIdByIdQuery = @"
+      WITH pbranch AS
+      (
+	      SELECT ProjectId, ParentProjectId, CAST(ProjectId AS NVARCHAR(MAX)) Path, 1 Distance
+	      FROM [Project]
+	      WHERE ProjectId = {0}
+	      UNION ALL
+	      SELECT pb.ProjectId, p.ParentProjectId, pb.Path + N', ' + CAST(pb.ProjectId AS NVARCHAR(MAX)), pb.Distance + 1
+	      FROM [Project] p
+	      JOIN pbranch pb ON pb.ParentProjectId = p.ProjectId AND p.ParentProjectId <> p.ProjectId AND pb.ParentProjectId <> pb.ProjectId
+      )
+      SELECT TOP(1) pro.*
+      FROM pbranch, [Project] pro
+      WHERE pbranch.ParentProjectId = pro.ProjectId
+      AND pro.OwnerUserId = {1}
+      ORDER BY pbranch.Distance DESC
+    ";
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ProjectPermissionDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ProjectPermissionDao.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ProjectPermissionDao.cs	(revision 16240)
@@ -0,0 +1,98 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Data.Linq;
+using System.Linq;
+
+namespace HeuristicLab.Services.Hive.DataAccess.Daos {
+  public class ProjectPermissionDao : GenericDao<Guid, ProjectPermission> {
+    public ProjectPermissionDao(DataContext dataContext) : base(dataContext) { }
+
+    public override ProjectPermission GetById(Guid id) {
+      throw new NotImplementedException();
+    }
+
+    public IEnumerable<ProjectPermission> GetByProjectId(Guid id) {
+      return GetByProjectIdGetByIdQuery(DataContext, id);
+    }
+
+    public bool CheckUserGrantedForProject(Guid projectId, IEnumerable<Guid> userAndGroupIds) {
+      string paramUserAndGroupIds = string.Join(",", userAndGroupIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if(!string.IsNullOrWhiteSpace(paramUserAndGroupIds)) {
+        string queryString = string.Format(CheckUserGrantedForProjectQueryString, projectId, paramUserAndGroupIds);
+        return DataContext.ExecuteQuery<int>(queryString).First() > 0;
+      }
+      return false;
+    }
+
+    public void DeleteByProjectIdAndGrantedUserIds(Guid projectId, IEnumerable<Guid> grantedUserIds) {
+      string paramIds = string.Join(",", grantedUserIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramIds)) {
+        string query = string.Format(DeleteByProjectIdAndGrantedUserIdsQueryString, projectId, paramIds);
+        DataContext.ExecuteCommand(query);
+      }
+    }
+
+    public void DeleteByProjectIdsAndGrantedUserIds(IEnumerable<Guid> projectIds, IEnumerable<Guid> grantedUserIds) {
+      string paramProjectIds = string.Join(",", projectIds.ToList().Select(x => string.Format("'{0}'", x)));
+      string paramUserIds = string.Join(",", grantedUserIds.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramProjectIds) && !string.IsNullOrWhiteSpace(paramUserIds)) {
+        string query = string.Format(DeleteByProjectIdsAndGrantedUserIdsQueryString, paramProjectIds, paramUserIds);
+        DataContext.ExecuteCommand(query);
+      }
+    }
+
+    #region Compiled queries
+    private static readonly Func<DataContext, Guid, IEnumerable<ProjectPermission>> GetByProjectIdGetByIdQuery =
+      CompiledQuery.Compile((DataContext db, Guid projectId) =>
+        from projectPermission in db.GetTable<ProjectPermission>()
+        where projectPermission.ProjectId == projectId
+        select projectPermission);
+    #endregion
+
+    #region String queries
+    private const string DeleteByProjectIdAndGrantedUserIdsQueryString = @"
+      DELETE FROM [ProjectPermission]
+      WHERE ProjectId = '{0}'
+      AND GrantedUserId IN ({1});
+    ";
+    private const string DeleteByProjectIdsAndGrantedUserIdsQueryString = @"
+      DELETE FROM [ProjectPermission]
+      WHERE ProjectId IN ({0})
+      AND GrantedUserId IN ({1});
+    ";
+    private const string CheckUserGrantedForProjectQueryString = @"
+      SELECT COUNT(pp.ProjectId)
+      FROM [ProjectPermission] pp
+      WHERE pp.ProjectId = '{0}'
+      AND pp.GrantedUserId IN ({1})
+    ";
+    private const string GetGrantedProjectsForUserQueryString = @"
+      SELECT DISTINCT p.*
+      FROM [ProjectPermission] pp, [Project] p
+      WHERE pp.GrantedUserId IN ({0})
+      AND  pp.ProjectId = p.ProjectId
+    ";
+    #endregion
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ResourceDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ResourceDao.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ResourceDao.cs	(revision 16240)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections.Generic;
 using System.Data.Linq;
 using System.Linq;
@@ -36,6 +37,47 @@
     }
 
+    public void DeleteByIds(IEnumerable<Guid> ids) {
+      string paramResourceIds = string.Join(",", ids.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string queryString = string.Format(DeleteByIdsQueryString, paramResourceIds);
+        DataContext.ExecuteCommand(queryString);
+      }
+    }
+
+    public bool CheckExistence(IEnumerable<Guid> ids) {
+      string paramResourceIds = string.Join(",", ids.ToList().Select(x => string.Format("'{0}'", x)));
+      if (!string.IsNullOrWhiteSpace(paramResourceIds)) {
+        string queryString = string.Format(CountExistenceQuery, paramResourceIds);
+        return DataContext.ExecuteQuery<int>(queryString).SingleOrDefault() == ids.Count();
+      }
+      return false;
+    }
+
     public IQueryable<Resource> GetResourcesWithValidOwner() {
       return Table.Where(x => x.OwnerUserId != null);
+    }
+
+    public IEnumerable<Resource> GetChildResourcesById(Guid id) {
+      return DataContext.ExecuteQuery<Resource>(GetChildResourcesByIdQuery, id);
+    }
+
+    public IEnumerable<Guid> GetChildResourceIdsById(Guid id) {
+      return DataContext.ExecuteQuery<Guid>(GetChildResourceIdsByIdQuery, id);
+    }
+
+    public IEnumerable<Resource> GetParentResourcesById(Guid id) {
+      return DataContext.ExecuteQuery<Resource>(GetParentResourcesByIdQuery, id);
+    }
+
+    public IEnumerable<Guid> GetParentResourceIdsById(Guid id) {
+      return DataContext.ExecuteQuery<Guid>(GetParentResourceIdsByIdQuery, id);
+    }
+
+    public IEnumerable<Resource> GetCurrentAndParentResourcesById(Guid id) {
+      return DataContext.ExecuteQuery<Resource>(GetCurrentAndParentResourcesByIdQuery, id);
+    }
+
+    public IEnumerable<Guid> GetCurrentAndParentResourceIdsById(Guid id) {
+      return DataContext.ExecuteQuery<Guid>(GetCurrentAndParentResourceIdsByIdQuery, id);
     }
 
@@ -53,4 +95,103 @@
         select resource).FirstOrDefault());
     #endregion
+
+    #region String queries
+    private const string DeleteByIdsQueryString = @"
+      DELETE FROM [Resource]
+      WHERE ResourceId IN ({0})
+    ";
+    private const string CountExistenceQuery = @"
+      SELECT COUNT(DISTINCT r.ResourceId)
+      FROM [Resource] r
+      WHERE r.ResourceId IN ({0})
+    ";
+    private const string GetChildResourcesByIdQuery = @"
+    	WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId AND r.ParentResourceId <> r.ResourceId AND rt.ParentResourceId <> rt.ResourceId
+      )
+      SELECT DISTINCT res.*
+      FROM rtree, [Resource] res
+      WHERE rtree.ParentResourceId = {0}
+	    AND rtree.ResourceId = res.ResourceId
+    ";
+    private const string GetChildResourceIdsByIdQuery = @"
+      WITH rtree AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rt.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rtree rt ON rt.ParentResourceId = r.ResourceId AND r.ParentResourceId <> r.ResourceId AND rt.ParentResourceId <> rt.ResourceId
+      )
+      SELECT DISTINCT rtree.ResourceId
+      FROM rtree
+      WHERE rtree.ParentResourceId = {0}
+    ";
+    private const string GetParentResourcesByIdQuery = @"
+      WITH rbranch AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rb.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId AND r.ParentResourceId <> r.ResourceId AND rb.ParentResourceId <> rb.ResourceId
+      )
+      SELECT DISTINCT res.*
+      FROM rbranch, [Resource] res
+      WHERE rbranch.ResourceId = {0}
+      AND rbranch.ParentResourceId = res.ResourceId
+    ";
+    private const string GetParentResourceIdsByIdQuery = @"
+      WITH rbranch AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      UNION ALL
+	      SELECT rb.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId AND r.ParentResourceId <> r.ResourceId AND rb.ParentResourceId <> rb.ResourceId
+      )
+      SELECT DISTINCT rbranch.ParentResourceId
+      FROM rbranch
+      WHERE rbranch.ResourceId = {0}
+    ";
+    private const string GetCurrentAndParentResourcesByIdQuery = @"
+      WITH rbranch AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      WHERE ResourceId = {0}
+	      UNION ALL
+	      SELECT r.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId
+      )
+      SELECT DISTINCT res.*
+      FROM rbranch, [Resource] res
+      WHERE rbranch.ResourceId = res.ResourceId
+    ";
+    private const string GetCurrentAndParentResourceIdsByIdQuery = @"
+      WITH rbranch AS
+      (
+	      SELECT ResourceId, ParentResourceId
+	      FROM [Resource]
+	      WHERE ResourceId = {0}
+	      UNION ALL
+	      SELECT r.ResourceId, r.ParentResourceId
+	      FROM [Resource] r
+	      JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId
+      )
+      SELECT DISTINCT rbranch.ResourceId
+      FROM rbranch
+    ";
+    #endregion
   }
 }
Index: anches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ResourcePermissionDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/ResourcePermissionDao.cs	(revision 16239)
+++ 	(revision )
@@ -1,62 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Data.Linq;
-using System.Linq;
-
-namespace HeuristicLab.Services.Hive.DataAccess.Daos {
-  public class ResourcePermissionDao : GenericDao<Guid, ResourcePermission> {
-    public ResourcePermissionDao(DataContext dataContext) : base(dataContext) { }
-
-    public override ResourcePermission GetById(Guid id) {
-      throw new NotImplementedException();
-    }
-
-    public IEnumerable<ResourcePermission> GetByResourceId(Guid id) {
-      return GetByResourceIdGetByIdQuery(DataContext, id);
-    }
-
-    public void DeleteByResourceAndGrantedUserId(Guid resourceId, IEnumerable<Guid> grantedUserId) {
-      string paramIds = string.Join(",", grantedUserId.Select(x => string.Format("'{0}'", x)));
-      if (!string.IsNullOrWhiteSpace(paramIds)) {
-        string query = string.Format(DeleteByGrantedUserQuery, resourceId, paramIds);
-        DataContext.ExecuteCommand(query);
-      }
-    }
-
-    #region Compiled queries
-    private static readonly Func<DataContext, Guid, IEnumerable<ResourcePermission>> GetByResourceIdGetByIdQuery =
-      CompiledQuery.Compile((DataContext db, Guid resourceId) =>
-        from resourcePermission in db.GetTable<ResourcePermission>()
-        where resourcePermission.ResourceId == resourceId
-        select resourcePermission);
-    #endregion
-
-    #region String queries
-    private const string DeleteByGrantedUserQuery =
-      @"DELETE FROM [ResourcePermission]
-         WHERE ResourceId = '{0}'
-           AND GrantedUserId IN ({1});";
-    #endregion
-  }
-}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDao.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDao.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDao.cs	(revision 16240)
@@ -27,8 +27,4 @@
 namespace HeuristicLab.Services.Hive.DataAccess.Daos {
   public class TaskDao : GenericDao<Guid, Task> {
-    private Table<AssignedResource> AssignedResourceTable {
-      get { return DataContext.GetTable<AssignedResource>(); }
-    }
-
     public TaskDao(DataContext dataContext) : base(dataContext) { }
 
@@ -54,5 +50,9 @@
       //Because there is at the moment no case where this makes sense (there don't exist parent tasks which need to be calculated), 
       //we skip this step because it's wasted runtime
-      return DataContext.ExecuteQuery<TaskPriorityInfo>(GetWaitingTasksQueryString, slave.ResourceId, Enum.GetName(typeof(TaskState), TaskState.Waiting), slave.FreeCores, slave.FreeMemory).ToList();
+      return DataContext.ExecuteQuery<TaskPriorityInfo>(GetWaitingTasksQueryString, 
+        slave.ResourceId, 
+        Enum.GetName(typeof(TaskState), TaskState.Waiting), 
+        slave.FreeCores, 
+        slave.FreeMemory).ToList();
     }
 
@@ -62,22 +62,19 @@
     /// <param name="resourceIds">list of resourceids which for which the task should be valid</param>
     /// <param name="count">maximum number of task to return</param>
-    /// <param name="finished">if true, all parent task which have FinishWhenChildJobsFinished=true are returned, otherwise only FinishWhenChildJobsFinished=false are returned</param>
+    /// <param name="finished">if true, all parent tasks which have FinishWhenChildJobsFinished=true are returned, otherwise only FinishWhenChildJobsFinished=false are returned</param>
     /// <returns></returns>
     public IEnumerable<Task> GetParentTasks(IEnumerable<Guid> resourceIds, int count, bool finished) {
-      var query = from ar in AssignedResourceTable
-                  where resourceIds.Contains(ar.ResourceId)
-                     && ar.Task.State == TaskState.Waiting
-                     && ar.Task.IsParentTask
-                     && (finished ? ar.Task.FinishWhenChildJobsFinished : !ar.Task.FinishWhenChildJobsFinished)
-                     && (from child in Table
-                         where child.ParentTaskId == ar.Task.TaskId
-                         select child.State == TaskState.Finished
-                             || child.State == TaskState.Aborted
-                             || child.State == TaskState.Failed).All(x => x)
-                     && (from child in Table // avoid returning WaitForChildTasks task where no child-task exist (yet)
-                         where child.ParentTaskId == ar.Task.TaskId
-                         select child).Any()
-                  orderby ar.Task.Priority descending
-                  select ar.Task;
+    var query = from t in Table
+                where t.State == TaskState.Waiting
+                    && t.IsParentTask
+                    && t.Job.AssignedJobResources.All(x => resourceIds.ToList().Contains(x.ResourceId))
+                    && t.FinishWhenChildJobsFinished == finished
+                    && t.ChildJobs.Any()
+                    && t.ChildJobs.All(x =>
+                      x.State == TaskState.Finished
+                      || x.State == TaskState.Aborted
+                      || x.State == TaskState.Failed)
+                  orderby t.Priority descending
+                  select t;
       return count == 0 ? query.ToArray() : query.Take(count).ToArray();
     }
@@ -96,6 +93,14 @@
 
     #region String queries
+    private const string GetCalculatingChildTasksByProjectId = @"
+      SELECT t.* FROM [Task] t, [Job] j
+        WHERE t.IsParentTask = 0
+        AND t.TaskState = 'Calculating'
+        AND t.JobId = j.JobId
+        AND j.ProjectId = {0}
+        ORDER BY j.ProjectId
+    ";
     private const string GetWaitingTasksQueryString = @"
-      WITH pr AS (
+      WITH rbranch AS (
         SELECT ResourceId, ParentResourceId
         FROM [Resource]
@@ -103,13 +108,17 @@
         UNION ALL
         SELECT r.ResourceId, r.ParentResourceId
-        FROM [Resource] r JOIN pr ON r.ResourceId = pr.ParentResourceId
+        FROM [Resource] r
+        JOIN rbranch rb ON rb.ParentResourceId = r.ResourceId
       )
       SELECT DISTINCT t.TaskId, t.JobId, t.Priority
-      FROM pr JOIN AssignedResources ar ON ar.ResourceId = pr.ResourceId
-          JOIN Task t ON t.TaskId = ar.TaskId
+      FROM [Task] t, [Job] j, [AssignedJobResource] ajr, rbranch
       WHERE NOT (t.IsParentTask = 1 AND t.FinishWhenChildJobsFinished = 1)
-          AND t.TaskState = {1}
-          AND t.CoresNeeded <= {2}
-          AND t.MemoryNeeded <= {3}
+      AND t.TaskState = {1}
+      AND t.CoresNeeded <= {2}
+      AND t.MemoryNeeded <= {3}
+      AND t.JobId = j.JobId
+      AND j.JobState = 'Online'
+      AND j.JobId = ajr.JobId
+      AND ajr.ResourceId = rbranch.ResourceId
     ";
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Enums/JobState.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Enums/JobState.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Enums/JobState.cs	(revision 16240)
@@ -0,0 +1,45 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+
+namespace HeuristicLab.Services.Hive.DataAccess {
+  [Serializable]
+  public enum JobState {
+    /// <summary>
+    /// A job is online as long as he is no other state.
+    /// </summary>
+    Online,
+
+    /// <summary>
+    /// A job is in StatisticsPending if its deletion has been requested,
+    /// but the final generation of statistics hasn't been performed yet.
+    /// </summary>
+    StatisticsPending,
+
+    /// <summary>
+    /// A job is in DeletionPending if its deletion has been requested,
+    /// the final generation of statistics has already been performed,
+    /// but the eventual deletion of the job is still pending. 
+    /// </summary>
+    DeletionPending
+  };
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HeuristicLab.Services.Hive.DataAccess-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HeuristicLab.Services.Hive.DataAccess-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HeuristicLab.Services.Hive.DataAccess-3.3.csproj	(revision 16240)
@@ -104,12 +104,15 @@
   <ItemGroup>
     <None Include="Plugin.cs.frame" />
-    <Compile Include="Daos\AssignedResourceDao.cs" />
+    <Compile Include="Daos\AssignedJobResourceDao.cs" />
+    <Compile Include="Daos\AssignedProjectResourceDao.cs" />
     <Compile Include="Daos\DowntimeDao.cs" />
     <Compile Include="Daos\GenericDao.cs" />
     <Compile Include="Daos\HiveStatistics\DimClientDao.cs" />
     <Compile Include="Daos\HiveStatistics\DimJobDao.cs" />
+    <Compile Include="Daos\HiveStatistics\DimProjectDao.cs" />
     <Compile Include="Daos\HiveStatistics\DimTimeDao.cs" />
     <Compile Include="Daos\HiveStatistics\DimUserDao.cs" />
     <Compile Include="Daos\HiveStatistics\FactClientInfoDao.cs" />
+    <Compile Include="Daos\HiveStatistics\FactProjectInfoDao.cs" />
     <Compile Include="Daos\HiveStatistics\FactTaskDao.cs" />
     <Compile Include="Daos\JobDao.cs" />
@@ -118,7 +121,8 @@
     <Compile Include="Daos\PluginDao.cs" />
     <Compile Include="Daos\PluginDataDao.cs" />
+    <Compile Include="Daos\ProjectDao.cs" />
+    <Compile Include="Daos\ProjectPermissionDao.cs" />
     <Compile Include="Daos\RequiredPluginDao.cs" />
     <Compile Include="Daos\ResourceDao.cs" />
-    <Compile Include="Daos\ResourcePermissionDao.cs" />
     <Compile Include="Daos\SlaveDao.cs" />
     <Compile Include="Daos\SlaveGroupDao.cs" />
@@ -128,4 +132,5 @@
     <Compile Include="Daos\UserPriorityDao.cs" />
     <Compile Include="Data\TableInformation.cs" />
+    <Compile Include="Enums\JobState.cs" />
     <Compile Include="Interfaces\IGenericDao.cs" />
     <Compile Include="Enums\Command.cs" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.dbml
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.dbml	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.dbml	(revision 16240)
@@ -1,10 +1,9 @@
 ﻿<?xml version="1.0" encoding="utf-8"?><Database Name="HeuristicLab.Hive" Class="HiveDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
-  <Connection Mode="AppSettings" ConnectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.3" SettingsObjectName="HeuristicLab.Services.Hive.DataAccess.Settings" SettingsPropertyName="HeuristicLab_Hive_LinqConnectionString" Provider="System.Data.SqlClient" />
-  <Table Name="dbo.AssignedResources" Member="AssignedResources">
-    <Type Name="AssignedResource">
+  <Table Name="dbo.AssignedProjectResource" Member="AssignedProjectResources">
+    <Type Name="AssignedProjectResource">
       <Column Name="ResourceId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
-      <Column Name="TaskId" Storage="_JobId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
-      <Association Name="Resource_AssignedResource" Member="Resource" ThisKey="ResourceId" OtherKey="ResourceId" Type="Resource" IsForeignKey="true" DeleteRule="CASCADE" />
-      <Association Name="Task_AssignedResource" Member="Task" Storage="_Job" ThisKey="TaskId" OtherKey="TaskId" Type="Task" IsForeignKey="true" DeleteRule="CASCADE" />
+      <Column Name="ProjectId" Storage="_JobId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Association Name="Resource_AssignedProjectResource" Member="Resource" ThisKey="ResourceId" OtherKey="ResourceId" Type="Resource" IsForeignKey="true" DeleteRule="CASCADE" />
+      <Association Name="Project_AssignedProjectResource" Member="Project" ThisKey="ProjectId" OtherKey="ProjectId" Type="Project" IsForeignKey="true" DeleteRule="CASCADE" />
     </Type>
   </Table>
@@ -38,9 +37,9 @@
       <Column Name="HbInterval" Type="System.Int32" DbType="Int" CanBeNull="false" />
       <Column Name="OwnerUserId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
-      <Association Name="Resource_AssignedResource" Member="AssignedResources" ThisKey="ResourceId" OtherKey="ResourceId" Type="AssignedResource" />
+      <Association Name="Resource_AssignedProjectResource" Member="AssignedProjectResources" ThisKey="ResourceId" OtherKey="ResourceId" Type="AssignedProjectResource" />
       <Association Name="Resource_Resource" Member="ChildResources" ThisKey="ResourceId" OtherKey="ParentResourceId" Type="Resource" />
       <Association Name="Resource_Downtime" Member="Downtimes" Storage="_UptimeCalendars" ThisKey="ResourceId" OtherKey="ResourceId" Type="Downtime" />
       <Association Name="Resource_StateLog" Member="StateLogs" ThisKey="ResourceId" OtherKey="SlaveId" Type="StateLog" />
-      <Association Name="Resource_ResourcePermission" Member="ResourcePermissions" ThisKey="ResourceId" OtherKey="ResourceId" Type="ResourcePermission" />
+      <Association Name="Resource_AssignedJobResource" Member="AssignedJobResources" ThisKey="ResourceId" OtherKey="ResourceId" Type="AssignedJobResource" />
       <Association Name="Resource_Resource" Member="ParentResource" ThisKey="ParentResourceId" OtherKey="ResourceId" Type="Resource" IsForeignKey="true" />
       <Type Name="Slave" InheritanceCode="Slave" IsInheritanceDefault="true">
@@ -76,5 +75,4 @@
       <Column Name="Command" Type="global::HeuristicLab.Services.Hive.DataAccess.Command?" DbType="VarChar(30)" CanBeNull="true" />
       <Column Name="JobId" Storage="_HiveExperimentId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
-      <Association Name="Task_AssignedResource" Member="AssignedResources" ThisKey="TaskId" OtherKey="TaskId" Type="AssignedResource" />
       <Association Name="Task_RequiredPlugin" Member="RequiredPlugins" ThisKey="TaskId" OtherKey="TaskId" Type="RequiredPlugin" />
       <Association Name="Task_Task" Member="ChildJobs" Storage="_Jobs" ThisKey="TaskId" OtherKey="ParentTaskId" Type="Task" />
@@ -103,9 +101,12 @@
       <Column Name="Name" Type="System.String" DbType="VarChar(MAX)" CanBeNull="false" />
       <Column Name="Description" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
-      <Column Name="ResourceIds" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
       <Column Name="OwnerUserId" Storage="_UserId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="false" />
       <Column Name="DateCreated" Type="System.DateTime" DbType="DateTime" CanBeNull="false" />
+      <Column Name="ProjectId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
+      <Column Name="JobState" Member="State" Type="global::HeuristicLab.Services.Hive.DataAccess.JobState" DbType="VarChar(30)" CanBeNull="false" />
       <Association Name="Job_Task" Member="Tasks" Storage="_Jobs" ThisKey="JobId" OtherKey="JobId" Type="Task" />
       <Association Name="Job_JobPermission" Member="JobPermissions" Storage="_HiveExperimentPermissions" ThisKey="JobId" OtherKey="JobId" Type="JobPermission" />
+      <Association Name="Job_AssignedJobResource" Member="AssignedJobResources" ThisKey="JobId" OtherKey="JobId" Type="AssignedJobResource" />
+      <Association Name="Project_Job" Member="Project" ThisKey="ProjectId" OtherKey="ProjectId" Type="Project" IsForeignKey="true" />
     </Type>
   </Table>
@@ -155,12 +156,4 @@
     </Type>
   </Table>
-  <Table Name="dbo.ResourcePermission" Member="ResourcePermissions">
-    <Type Name="ResourcePermission">
-      <Column Name="ResourceId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
-      <Column Name="GrantedUserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
-      <Column Name="GrantedByUserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
-      <Association Name="Resource_ResourcePermission" Member="Resource" ThisKey="ResourceId" OtherKey="ResourceId" Type="Resource" IsForeignKey="true" />
-    </Type>
-  </Table>
   <Table Name="" Member="UserPriorities">
     <Type Name="UserPriority">
@@ -174,9 +167,8 @@
       <Column Name="Name" Type="System.String" DbType="VarChar(MAX) NOT NULL" CanBeNull="false" />
       <Column Name="ResourceId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
-      <Column Name="ExpirationTime" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
-      <Column Name="ResourceGroupId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
-      <Column Name="ResourceGroup2Id" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
-      <Column Name="GroupName" Type="System.String" CanBeNull="true" />
-      <Column Name="GroupName2" Type="System.String" CanBeNull="true" />
+      <Column Name="ParentResourceId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
+      <Column Name="ResourceType" Type="System.String" CanBeNull="false" />
+      <Column Name="DateCreated" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" />
+      <Column Name="DateExpired" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
       <Association Name="DimClient_FactTask" Member="FactTasks" ThisKey="Id" OtherKey="LastClientId" Type="FactTask" />
       <Association Name="DimClient_FactClientInfo" Member="FactClientInfos" ThisKey="Id" OtherKey="ClientId" Type="FactClientInfo" />
@@ -215,5 +207,7 @@
       <Column Name="CompletedTasks" Type="System.Int32" DbType="INT NOT NULL" CanBeNull="false" />
       <Column Name="DateCompleted" Type="System.DateTime" DbType="DateTime NULL" CanBeNull="true" />
+      <Column Name="ProjectId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
       <Association Name="DimJob_FactTask" Member="FactTasks" ThisKey="JobId" OtherKey="JobId" Type="FactTask" />
+      <Association Name="DimProject_DimJob" Member="DimProject" ThisKey="ProjectId" OtherKey="Id" Type="DimProject" IsForeignKey="true" />
     </Type>
   </Table>
@@ -227,4 +221,5 @@
       <Column Name="Minute" Type="System.DateTime" CanBeNull="false" />
       <Association Name="DimTime_FactClientInfo" Member="FactClientInfos" ThisKey="Time" OtherKey="Time" Type="FactClientInfo" />
+      <Association Name="DimTime_FactProjectInfo" Member="FactProjectInfos" ThisKey="Time" OtherKey="Time" Type="FactProjectInfo" />
     </Type>
   </Table>
@@ -240,5 +235,5 @@
       <Column Name="ClientId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
       <Column Name="Time" Type="System.DateTime" DbType="DateTime NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
-      <Column Name="UserId" Type="System.Guid" DbType="UniqueIdentifier NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="UserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
       <Column Name="NumUsedCores" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
       <Column Name="NumTotalCores" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
@@ -256,3 +251,64 @@
     </Type>
   </Table>
+  <Table Name="dbo.Project" Member="Projects">
+    <Type Name="Project">
+      <Column Name="ProjectId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
+      <Column Name="ParentProjectId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
+      <Column Name="DateCreated" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" />
+      <Column Name="Name" Type="System.String" DbType="VarChar(MAX) NOT NULL" CanBeNull="false" />
+      <Column Name="Description" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
+      <Column Name="OwnerUserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
+      <Column Name="StartDate" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" />
+      <Column Name="EndDate" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
+      <Association Name="Project_AssignedProjectResource" Member="AssignedProjectResources" ThisKey="ProjectId" OtherKey="ProjectId" Type="AssignedProjectResource" />
+      <Association Name="Project_Job" Member="Jobs" ThisKey="ProjectId" OtherKey="ProjectId" Type="Job" />
+      <Association Name="Project_Project" Member="ChildProjects" Storage="_Projects" ThisKey="ProjectId" OtherKey="ParentProjectId" Type="Project" />
+      <Association Name="Project_ProjectPermission" Member="ProjectPermissions" ThisKey="ProjectId" OtherKey="ProjectId" Type="ProjectPermission" />
+      <Association Name="Project_Project" Member="ParentProject" Storage="_Project1" ThisKey="ParentProjectId" OtherKey="ProjectId" Type="Project" IsForeignKey="true" />
+    </Type>
+  </Table>
+  <Table Name="dbo.ProjectPermission" Member="ProjectPermissions">
+    <Type Name="ProjectPermission">
+      <Column Name="ProjectId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="GrantedUserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="GrantedByUserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
+      <Association Name="Project_ProjectPermission" Member="Project" ThisKey="ProjectId" OtherKey="ProjectId" Type="Project" IsForeignKey="true" />
+    </Type>
+  </Table>
+  <Table Name="dbo.AssignedJobResource" Member="AssignedJobResources">
+    <Type Name="AssignedJobResource">
+      <Column Name="ResourceId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="JobId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Association Name="Resource_AssignedJobResource" Member="Resource" ThisKey="ResourceId" OtherKey="ResourceId" Type="Resource" IsForeignKey="true" />
+      <Association Name="Job_AssignedJobResource" Member="Job" ThisKey="JobId" OtherKey="JobId" Type="Job" IsForeignKey="true" />
+    </Type>
+  </Table>
+  <Table Name="[statistics].FactProjectInfo" Member="FactProjectInfos">
+    <Type Name="FactProjectInfo">
+      <Column Name="ProjectId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="Time" Type="System.DateTime" DbType="DateTime NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="NumTotalCores" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
+      <Column Name="NumUsedCores" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
+      <Column Name="TotalMemory" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
+      <Column Name="UsedMemory" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
+      <Association Name="DimProject_FactProjectInfo" Member="DimProject" ThisKey="ProjectId" OtherKey="Id" Type="DimProject" IsForeignKey="true" />
+      <Association Name="DimTime_FactProjectInfo" Member="DimTime" ThisKey="Time" OtherKey="Time" Type="DimTime" IsForeignKey="true" />
+    </Type>
+  </Table>
+  <Table Name="[statistics].DimProject" Member="DimProjects">
+    <Type Name="DimProject">
+      <Column Name="Id" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
+      <Column Name="ProjectId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
+      <Column Name="ParentProjectId" Storage="_ParentId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
+      <Column Name="Name" Type="System.String" CanBeNull="false" />
+      <Column Name="Description" Type="System.String" CanBeNull="true" />
+      <Column Name="OwnerUserId" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" CanBeNull="false" />
+      <Column Name="StartDate" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" />
+      <Column Name="EndDate" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
+      <Column Name="DateCreated" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" />
+      <Column Name="DateExpired" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
+      <Association Name="DimProject_DimJob" Member="DimJobs" ThisKey="Id" OtherKey="ProjectId" Type="DimJob" />
+      <Association Name="DimProject_FactProjectInfo" Member="FactProjectInfos" ThisKey="Id" OtherKey="ProjectId" Type="FactProjectInfo" />
+    </Type>
+  </Table>
 </Database>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.dbml.layout
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.dbml.layout	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.dbml.layout	(revision 16240)
@@ -1,10 +1,10 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
-<ordesignerObjectsDiagram dslVersion="1.0.0.0" absoluteBounds="0, 0, 15.75, 17.875" name="HiveDataContext">
+<ordesignerObjectsDiagram dslVersion="1.0.0.0" absoluteBounds="0, 0, 16.25, 23.875" name="HiveDataContext">
   <DataContextMoniker Name="/HiveDataContext" />
   <nestedChildShapes>
-    <classShape Id="a929c9dc-69f4-4488-ba1c-a2342bf81d89" absoluteBounds="8.875, 4.5, 2, 1.1939925130208327">
-      <DataClassMoniker Name="/HiveDataContext/AssignedResource" />
-      <nestedChildShapes>
-        <elementListCompartment Id="8b005775-f0ee-41b0-ae10-6d1151003708" absoluteBounds="8.89, 4.9600000000000009, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+    <classShape Id="a929c9dc-69f4-4488-ba1c-a2342bf81d89" absoluteBounds="13.875, 8.25, 2, 1.1939925130208327">
+      <DataClassMoniker Name="/HiveDataContext/AssignedProjectResource" />
+      <nestedChildShapes>
+        <elementListCompartment Id="8b005775-f0ee-41b0-ae10-6d1151003708" absoluteBounds="13.89, 8.71, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
       </nestedChildShapes>
     </classShape>
@@ -39,8 +39,8 @@
       </nestedChildShapes>
     </classShape>
-    <classShape Id="e6f840cc-2968-4be1-b234-eef624ccacbb" absoluteBounds="4.125, 2.625, 2, 1.9631982421874996">
+    <classShape Id="e6f840cc-2968-4be1-b234-eef624ccacbb" absoluteBounds="4.125, 2.625, 2, 2.1554996744791666">
       <DataClassMoniker Name="/HiveDataContext/Job" />
       <nestedChildShapes>
-        <elementListCompartment Id="0c65d4e1-256a-4a91-9a57-392f25e4de7f" absoluteBounds="4.1400000000000006, 3.0850000000000009, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+        <elementListCompartment Id="0c65d4e1-256a-4a91-9a57-392f25e4de7f" absoluteBounds="4.1400000000000006, 3.0850000000000009, 1.9700000000000002, 1.5954996744791665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
       </nestedChildShapes>
     </classShape>
@@ -69,11 +69,4 @@
       </nodes>
     </inheritanceConnector>
-    <associationConnector edgePoints="[(11.8134963640734 : 2.9631982421875); (11.8134963640734 : 3.56770833333333); (11.1119791666667 : 3.56770833333333); (11.1119791666667 : 5.09699625651042); (10.875 : 5.09699625651042)]" manuallyRouted="true" fixedFrom="NotFixed" fixedTo="NotFixed">
-      <AssociationMoniker Name="/HiveDataContext/Resource/Resource_AssignedResource" />
-      <nodes>
-        <classShapeMoniker Id="706a4581-6daf-4e71-ae2a-87d50b27a051" />
-        <classShapeMoniker Id="a929c9dc-69f4-4488-ba1c-a2342bf81d89" />
-      </nodes>
-    </associationConnector>
     <classShape Id="6bc13f26-f9a8-4597-b054-35be34190d12" absoluteBounds="4.125, 1, 2, 1.3862939453125">
       <DataClassMoniker Name="/HiveDataContext/TaskData" />
@@ -141,12 +134,5 @@
       </nodes>
     </associationConnector>
-    <associationConnector edgePoints="[(8.5 : 4.1170068359375); (8.875 : 4.5)]" fixedFrom="NotFixed" fixedTo="NotFixed">
-      <AssociationMoniker Name="/HiveDataContext/Task/Task_AssignedResource" />
-      <nodes>
-        <classShapeMoniker Id="695bfc39-59f3-4e60-8644-f847964bf62c" />
-        <classShapeMoniker Id="a929c9dc-69f4-4488-ba1c-a2342bf81d89" />
-      </nodes>
-    </associationConnector>
-    <associationConnector edgePoints="[(7.4687475 : 4.1170068359375); (7.4687475 : 5.875)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+    <associationConnector edgePoints="[(7.5 : 4.1170068359375); (7.5 : 5.875)]" fixedFrom="NotFixed" fixedTo="NotFixed">
       <AssociationMoniker Name="/HiveDataContext/Task/Task_RequiredPlugin" />
       <nodes>
@@ -183,5 +169,5 @@
       </nodes>
     </associationConnector>
-    <associationConnector edgePoints="[(4.125 : 4.10659912109375); (3.75 : 4.10659912109375)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+    <associationConnector edgePoints="[(4.125 : 4.20274983723958); (3.75 : 4.20274983723958)]" fixedFrom="NotFixed" fixedTo="NotFixed">
       <AssociationMoniker Name="/HiveDataContext/Job/Job_JobPermission" />
       <nodes>
@@ -190,17 +176,4 @@
       </nodes>
     </associationConnector>
-    <classShape Id="a3f352be-9f15-4e73-8d44-3e8ac02fb4cf" absoluteBounds="11.25, 3.875, 2, 1.3862939453124996">
-      <DataClassMoniker Name="/HiveDataContext/ResourcePermission" />
-      <nestedChildShapes>
-        <elementListCompartment Id="45e2f1a8-8a1e-4647-b649-10ec55976ab4" absoluteBounds="11.265, 4.335, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <associationConnector edgePoints="[(12.5942481820367 : 2.9631982421875); (12.5942481820367 : 3.875)]" fixedFrom="NotFixed" fixedTo="NotFixed">
-      <AssociationMoniker Name="/HiveDataContext/Resource/Resource_ResourcePermission" />
-      <nodes>
-        <classShapeMoniker Id="706a4581-6daf-4e71-ae2a-87d50b27a051" />
-        <classShapeMoniker Id="a3f352be-9f15-4e73-8d44-3e8ac02fb4cf" />
-      </nodes>
-    </associationConnector>
     <classShape Id="f9e8867f-fd15-4a72-8ca4-4f02cd3f141f" absoluteBounds="4.125, 5.5, 2, 1.1939925130208327">
       <DataClassMoniker Name="/HiveDataContext/UserPriority" />
@@ -209,41 +182,41 @@
       </nestedChildShapes>
     </classShape>
-    <classShape Id="b5b919c2-4efc-4f09-8f52-9d541a11e961" absoluteBounds="4.625, 15.25, 2, 2.3478011067708344">
+    <classShape Id="b5b919c2-4efc-4f09-8f52-9d541a11e961" absoluteBounds="5.5, 12.25, 2, 2.1554996744791666">
       <DataClassMoniker Name="/HiveDataContext/DimClient" />
       <nestedChildShapes>
-        <elementListCompartment Id="30f62a7b-0b16-404e-b972-fb12bfe978dd" absoluteBounds="4.6400000000000006, 15.71, 1.9700000000000002, 1.7878011067708333" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <classShape Id="04f52807-ce17-4d65-bd23-cc38c6dfbd7a" absoluteBounds="6.875, 11.25, 2, 3.8862125651041666">
+        <elementListCompartment Id="30f62a7b-0b16-404e-b972-fb12bfe978dd" absoluteBounds="5.5150000000000006, 12.71, 1.9700000000000002, 1.5954996744791665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="04f52807-ce17-4d65-bd23-cc38c6dfbd7a" absoluteBounds="8.25, 12.25, 2, 3.8862125651041666">
       <DataClassMoniker Name="/HiveDataContext/FactTask" />
       <nestedChildShapes>
-        <elementListCompartment Id="63e3ddcb-a6fe-48e0-a674-e650a55a9f2c" absoluteBounds="6.8900000000000006, 11.71, 1.9700000000000002, 3.3262125651041665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <classShape Id="e0cb8641-a75e-4b9f-beda-3218c56938b1" absoluteBounds="8.125, 8.25, 2, 2.3478011067708344">
+        <elementListCompartment Id="63e3ddcb-a6fe-48e0-a674-e650a55a9f2c" absoluteBounds="8.265, 12.71, 1.9700000000000002, 3.3262125651041665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="e0cb8641-a75e-4b9f-beda-3218c56938b1" absoluteBounds="11.125, 12.25, 2, 2.5401025390625005">
       <DataClassMoniker Name="/HiveDataContext/DimJob" />
       <nestedChildShapes>
-        <elementListCompartment Id="6b9e8260-7e4b-4357-9c26-eebebfd69504" absoluteBounds="8.14, 8.71, 1.9700000000000002, 1.7878011067708333" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <classShape Id="a769f2ac-c8e3-4860-baa5-c46f46c38ed8" absoluteBounds="3.625, 8.5, 2, 1.9631982421875005">
+        <elementListCompartment Id="6b9e8260-7e4b-4357-9c26-eebebfd69504" absoluteBounds="11.14, 12.71, 1.9700000000000002, 1.9801025390625" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="a769f2ac-c8e3-4860-baa5-c46f46c38ed8" absoluteBounds="10, 21.625, 2, 1.9631982421875005">
       <DataClassMoniker Name="/HiveDataContext/DimTime" />
       <nestedChildShapes>
-        <elementListCompartment Id="694c4a5a-20fb-43a9-bc9a-7b59d794a7fb" absoluteBounds="3.6399999999999997, 8.96, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <classShape Id="66c34ced-4fdb-4a1d-b8dd-fe094679b7fb" absoluteBounds="0.625, 8.875, 2, 1.1939925130208344">
+        <elementListCompartment Id="694c4a5a-20fb-43a9-bc9a-7b59d794a7fb" absoluteBounds="10.015, 22.085, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="66c34ced-4fdb-4a1d-b8dd-fe094679b7fb" absoluteBounds="4.125, 21.625, 2, 1.1939925130208344">
       <DataClassMoniker Name="/HiveDataContext/DimUser" />
       <nestedChildShapes>
-        <elementListCompartment Id="6181c560-fc74-4d43-b064-a90032bf17a5" absoluteBounds="0.64000000000000012, 9.335, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <classShape Id="053a5cff-b18a-4ee5-8d43-48ed5c5dcfad" absoluteBounds="2.625, 11, 2, 3.3093082682291666">
+        <elementListCompartment Id="6181c560-fc74-4d43-b064-a90032bf17a5" absoluteBounds="4.1400000000000006, 22.085, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="053a5cff-b18a-4ee5-8d43-48ed5c5dcfad" absoluteBounds="5.5, 17, 2, 3.3093082682291666">
       <DataClassMoniker Name="/HiveDataContext/FactClientInfo" />
       <nestedChildShapes>
-        <elementListCompartment Id="0a1cc913-6636-455c-95b3-302f533527db" absoluteBounds="2.6399999999999997, 11.46, 1.9700000000000002, 2.7493082682291665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
-      </nestedChildShapes>
-    </classShape>
-    <associationConnector edgePoints="[(6.625 : 15.25); (6.875 : 15.1362125651042)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+        <elementListCompartment Id="0a1cc913-6636-455c-95b3-302f533527db" absoluteBounds="5.515, 17.46, 1.9700000000000002, 2.7493082682291665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <associationConnector edgePoints="[(7.5 : 13.3277498372396); (8.25 : 13.3277498372396)]" fixedFrom="Algorithm" fixedTo="Algorithm">
       <AssociationMoniker Name="/HiveDataContext/DimClient/DimClient_FactTask" />
       <nodes>
@@ -252,5 +225,5 @@
       </nodes>
     </associationConnector>
-    <associationConnector edgePoints="[(8.5 : 10.5978011067708); (8.5 : 11.25)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+    <associationConnector edgePoints="[(11.125 : 13.5200512695313); (10.25 : 13.5200512695313)]" fixedFrom="NotFixed" fixedTo="NotFixed">
       <AssociationMoniker Name="/HiveDataContext/DimJob/DimJob_FactTask" />
       <nodes>
@@ -259,5 +232,5 @@
       </nodes>
     </associationConnector>
-    <associationConnector edgePoints="[(5.6249975 : 15.25); (5.6249975 : 12.6546541341146); (4.625 : 12.6546541341146)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+    <associationConnector edgePoints="[(6.5 : 14.4054996744792); (6.5 : 17)]" fixedFrom="Algorithm" fixedTo="Algorithm">
       <AssociationMoniker Name="/HiveDataContext/DimClient/DimClient_FactClientInfo" />
       <nodes>
@@ -266,5 +239,5 @@
       </nodes>
     </associationConnector>
-    <associationConnector edgePoints="[(4.125 : 10.4631982421875); (4.125 : 11)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+    <associationConnector edgePoints="[(11 : 21.625); (11 : 18.6546541341146); (7.5 : 18.6546541341146)]" fixedFrom="NotFixed" fixedTo="NotFixed">
       <AssociationMoniker Name="/HiveDataContext/DimTime/DimTime_FactClientInfo" />
       <nodes>
@@ -273,5 +246,5 @@
       </nodes>
     </associationConnector>
-    <associationConnector edgePoints="[(1.59375 : 10.0689925130208); (1.59375 : 12.6546541341146); (2.625 : 12.6546541341146)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+    <associationConnector edgePoints="[(5.8125 : 21.625); (5.8125 : 20.3093082682292)]" fixedFrom="NotFixed" fixedTo="NotFixed">
       <AssociationMoniker Name="/HiveDataContext/DimUser/DimUser_FactClientInfo" />
       <nodes>
@@ -280,4 +253,104 @@
       </nodes>
     </associationConnector>
+    <classShape Id="30778876-b5f6-4a97-9ec7-792011973d75" absoluteBounds="11.25, 8.25, 2, 2.3478011067708344">
+      <DataClassMoniker Name="/HiveDataContext/Project" />
+      <nestedChildShapes>
+        <elementListCompartment Id="2c8b0555-6cea-4cc9-b028-2d066c02401c" absoluteBounds="11.265, 8.71, 1.9700000000000002, 1.7878011067708333" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <associationConnector edgePoints="[(11.9592307692308 : 10.5978011067708); (11.9592307692308 : 10.8478011067708); (12.5546153846154 : 10.8478011067708); (12.5546153846154 : 10.5978011067708)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Project/Project_Project" />
+      <nodes>
+        <classShapeMoniker Id="30778876-b5f6-4a97-9ec7-792011973d75" />
+        <classShapeMoniker Id="30778876-b5f6-4a97-9ec7-792011973d75" />
+      </nodes>
+    </associationConnector>
+    <classShape Id="1c20ff2a-adeb-4830-9456-9a2a3e11cee3" absoluteBounds="13.875, 9.875, 2, 1.3862939453125005">
+      <DataClassMoniker Name="/HiveDataContext/ProjectPermission" />
+      <nestedChildShapes>
+        <elementListCompartment Id="d115744c-dfef-4a5d-9b9a-30efc6ef4423" absoluteBounds="13.89, 10.335, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <associationConnector edgePoints="[(13.25 : 10.2364005533854); (13.875 : 10.2364005533854)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Project/Project_ProjectPermission" />
+      <nodes>
+        <classShapeMoniker Id="30778876-b5f6-4a97-9ec7-792011973d75" />
+        <classShapeMoniker Id="1c20ff2a-adeb-4830-9456-9a2a3e11cee3" />
+      </nodes>
+    </associationConnector>
+    <associationConnector edgePoints="[(11.25 : 9.42390055338542); (4 : 9.42390055338542); (4 : 5.28049967447917); (5.125 : 5.28049967447917); (5.125 : 4.78049967447917)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Project/Project_Job" />
+      <nodes>
+        <classShapeMoniker Id="30778876-b5f6-4a97-9ec7-792011973d75" />
+        <classShapeMoniker Id="e6f840cc-2968-4be1-b234-eef624ccacbb" />
+      </nodes>
+    </associationConnector>
+    <associationConnector edgePoints="[(13.25 : 2.15469482421875); (15.71875 : 2.15469482421875); (15.71875 : 8.25)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Resource/Resource_AssignedProjectResource" />
+      <nodes>
+        <classShapeMoniker Id="706a4581-6daf-4e71-ae2a-87d50b27a051" />
+        <classShapeMoniker Id="a929c9dc-69f4-4488-ba1c-a2342bf81d89" />
+      </nodes>
+    </associationConnector>
+    <associationConnector edgePoints="[(13.25 : 8.84699625651042); (13.875 : 8.84699625651042)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Project/Project_AssignedProjectResource" />
+      <nodes>
+        <classShapeMoniker Id="30778876-b5f6-4a97-9ec7-792011973d75" />
+        <classShapeMoniker Id="a929c9dc-69f4-4488-ba1c-a2342bf81d89" />
+      </nodes>
+    </associationConnector>
+    <classShape Id="0a400ab3-5f14-4eea-88f0-94420c71a7fb" absoluteBounds="8.875, 4.5, 2, 1.1939925130208327">
+      <DataClassMoniker Name="/HiveDataContext/AssignedJobResource" />
+      <nestedChildShapes>
+        <elementListCompartment Id="8ef99b6f-aa50-465a-bb2b-3b9b9698d417" absoluteBounds="8.89, 4.96, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <associationConnector edgePoints="[(12.25 : 2.9631982421875); (12.25 : 5.09699625651042); (10.875 : 5.09699625651042)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Resource/Resource_AssignedJobResource" />
+      <nodes>
+        <classShapeMoniker Id="706a4581-6daf-4e71-ae2a-87d50b27a051" />
+        <classShapeMoniker Id="0a400ab3-5f14-4eea-88f0-94420c71a7fb" />
+      </nodes>
+    </associationConnector>
+    <associationConnector edgePoints="[(5.125 : 4.78049967447917); (5.125 : 5.14024983723958); (7.41666666666667 : 5.14024983723958 : JumpStart); (7.58333333333333 : 5.14024983723958 : JumpEnd); (8.875 : 5.14024983723958)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/Job/Job_AssignedJobResource" />
+      <nodes>
+        <classShapeMoniker Id="e6f840cc-2968-4be1-b234-eef624ccacbb" />
+        <classShapeMoniker Id="0a400ab3-5f14-4eea-88f0-94420c71a7fb" />
+      </nodes>
+    </associationConnector>
+    <classShape Id="e261c9ea-27ec-47d1-8e68-aba5f835f0dd" absoluteBounds="13.75, 17, 2, 1.9631982421875023">
+      <DataClassMoniker Name="/HiveDataContext/FactProjectInfo" />
+      <nestedChildShapes>
+        <elementListCompartment Id="4ba36f1f-053c-478d-b9b4-d0a2b63c3129" absoluteBounds="13.765, 17.46, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="55ff3cee-22ff-48f2-a8ff-ccb8e2e84af9" absoluteBounds="13.75, 12.25, 2, 2.73240397135417">
+      <DataClassMoniker Name="/HiveDataContext/DimProject" />
+      <nestedChildShapes>
+        <elementListCompartment Id="cf69e9f9-115a-42d8-8d53-761a1edfc941" absoluteBounds="13.765, 12.71, 1.9700000000000002, 2.1724039713541665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <associationConnector edgePoints="[(14.75 : 14.9824039713542); (14.75 : 17)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/DimProject/DimProject_FactProjectInfo" />
+      <nodes>
+        <classShapeMoniker Id="55ff3cee-22ff-48f2-a8ff-ccb8e2e84af9" />
+        <classShapeMoniker Id="e261c9ea-27ec-47d1-8e68-aba5f835f0dd" />
+      </nodes>
+    </associationConnector>
+    <associationConnector edgePoints="[(11.5312525 : 21.625); (11.5312525 : 17.8273270670573); (13.75 : 17.8273270670573)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/DimTime/DimTime_FactProjectInfo" />
+      <nodes>
+        <classShapeMoniker Id="a769f2ac-c8e3-4860-baa5-c46f46c38ed8" />
+        <classShapeMoniker Id="e261c9ea-27ec-47d1-8e68-aba5f835f0dd" />
+      </nodes>
+    </associationConnector>
+    <associationConnector edgePoints="[(13.75 : 13.5200512695313); (13.125 : 13.5200512695313)]" fixedFrom="NotFixed" fixedTo="NotFixed">
+      <AssociationMoniker Name="/HiveDataContext/DimProject/DimProject_DimJob" />
+      <nodes>
+        <classShapeMoniker Id="55ff3cee-22ff-48f2-a8ff-ccb8e2e84af9" />
+        <classShapeMoniker Id="e0cb8641-a75e-4b9f-beda-3218c56938b1" />
+      </nodes>
+    </associationConnector>
   </nestedChildShapes>
 </ordesignerObjectsDiagram>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/HiveDataContext.designer.cs	(revision 16240)
@@ -3,5 +3,5 @@
 // <auto-generated>
 //     This code was generated by a tool.
-//     Runtime Version:4.0.30319.34209
+//     Runtime Version:4.0.30319.42000
 //
 //     Changes to this file may cause incorrect behavior and will be lost if
@@ -31,7 +31,7 @@
     #region Extensibility Method Definitions
     partial void OnCreated();
-    partial void InsertAssignedResource(AssignedResource instance);
-    partial void UpdateAssignedResource(AssignedResource instance);
-    partial void DeleteAssignedResource(AssignedResource instance);
+    partial void InsertAssignedProjectResource(AssignedProjectResource instance);
+    partial void UpdateAssignedProjectResource(AssignedProjectResource instance);
+    partial void DeleteAssignedProjectResource(AssignedProjectResource instance);
     partial void InsertPlugin(Plugin instance);
     partial void UpdatePlugin(Plugin instance);
@@ -67,7 +67,4 @@
     partial void UpdateLifecycle(Lifecycle instance);
     partial void DeleteLifecycle(Lifecycle instance);
-    partial void InsertResourcePermission(ResourcePermission instance);
-    partial void UpdateResourcePermission(ResourcePermission instance);
-    partial void DeleteResourcePermission(ResourcePermission instance);
     partial void InsertUserPriority(UserPriority instance);
     partial void UpdateUserPriority(UserPriority instance);
@@ -91,11 +88,20 @@
     partial void UpdateFactClientInfo(FactClientInfo instance);
     partial void DeleteFactClientInfo(FactClientInfo instance);
+    partial void InsertProject(Project instance);
+    partial void UpdateProject(Project instance);
+    partial void DeleteProject(Project instance);
+    partial void InsertProjectPermission(ProjectPermission instance);
+    partial void UpdateProjectPermission(ProjectPermission instance);
+    partial void DeleteProjectPermission(ProjectPermission instance);
+    partial void InsertAssignedJobResource(AssignedJobResource instance);
+    partial void UpdateAssignedJobResource(AssignedJobResource instance);
+    partial void DeleteAssignedJobResource(AssignedJobResource instance);
+    partial void InsertFactProjectInfo(FactProjectInfo instance);
+    partial void UpdateFactProjectInfo(FactProjectInfo instance);
+    partial void DeleteFactProjectInfo(FactProjectInfo instance);
+    partial void InsertDimProject(DimProject instance);
+    partial void UpdateDimProject(DimProject instance);
+    partial void DeleteDimProject(DimProject instance);
     #endregion
-		
-		public HiveDataContext() : 
-				base(global::HeuristicLab.Services.Hive.DataAccess.Settings.Default.HeuristicLab_Hive_LinqConnectionString, mappingSource)
-		{
-			OnCreated();
-		}
 		
 		public HiveDataContext(string connection) : 
@@ -123,9 +129,9 @@
 		}
 		
-		public System.Data.Linq.Table<AssignedResource> AssignedResources
-		{
-			get
-			{
-				return this.GetTable<AssignedResource>();
+		public System.Data.Linq.Table<AssignedProjectResource> AssignedProjectResources
+		{
+			get
+			{
+				return this.GetTable<AssignedProjectResource>();
 			}
 		}
@@ -219,12 +225,4 @@
 		}
 		
-		public System.Data.Linq.Table<ResourcePermission> ResourcePermissions
-		{
-			get
-			{
-				return this.GetTable<ResourcePermission>();
-			}
-		}
-		
 		public System.Data.Linq.Table<UserPriority> UserPriorities
 		{
@@ -280,10 +278,50 @@
 			{
 				return this.GetTable<FactClientInfo>();
+			}
+		}
+		
+		public System.Data.Linq.Table<Project> Projects
+		{
+			get
+			{
+				return this.GetTable<Project>();
+			}
+		}
+		
+		public System.Data.Linq.Table<ProjectPermission> ProjectPermissions
+		{
+			get
+			{
+				return this.GetTable<ProjectPermission>();
+			}
+		}
+		
+		public System.Data.Linq.Table<AssignedJobResource> AssignedJobResources
+		{
+			get
+			{
+				return this.GetTable<AssignedJobResource>();
+			}
+		}
+		
+		public System.Data.Linq.Table<FactProjectInfo> FactProjectInfos
+		{
+			get
+			{
+				return this.GetTable<FactProjectInfo>();
+			}
+		}
+		
+		public System.Data.Linq.Table<DimProject> DimProjects
+		{
+			get
+			{
+				return this.GetTable<DimProject>();
 			}
 		}
 	}
 	
-	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.AssignedResources")]
-	public partial class AssignedResource : INotifyPropertyChanging, INotifyPropertyChanged
+	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.AssignedProjectResource")]
+	public partial class AssignedProjectResource : INotifyPropertyChanging, INotifyPropertyChanged
 	{
 		
@@ -296,5 +334,5 @@
 		private EntityRef<Resource> _Resource;
 		
-		private EntityRef<Task> _Job;
+		private EntityRef<Project> _Project;
 		
     #region Extensibility Method Definitions
@@ -304,12 +342,12 @@
     partial void OnResourceIdChanging(System.Guid value);
     partial void OnResourceIdChanged();
-    partial void OnTaskIdChanging(System.Guid value);
-    partial void OnTaskIdChanged();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
     #endregion
 		
-		public AssignedResource()
+		public AssignedProjectResource()
 		{
 			this._Resource = default(EntityRef<Resource>);
-			this._Job = default(EntityRef<Task>);
+			this._Project = default(EntityRef<Project>);
 			OnCreated();
 		}
@@ -340,5 +378,5 @@
 		
 		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_JobId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
-		public System.Guid TaskId
+		public System.Guid ProjectId
 		{
 			get
@@ -350,18 +388,18 @@
 				if ((this._JobId != value))
 				{
-					if (this._Job.HasLoadedOrAssignedValue)
+					if (this._Project.HasLoadedOrAssignedValue)
 					{
 						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
 					}
-					this.OnTaskIdChanging(value);
+					this.OnProjectIdChanging(value);
 					this.SendPropertyChanging();
 					this._JobId = value;
-					this.SendPropertyChanged("TaskId");
-					this.OnTaskIdChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_AssignedResource", Storage="_Resource", ThisKey="ResourceId", OtherKey="ResourceId", IsForeignKey=true, DeleteRule="CASCADE")]
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_AssignedProjectResource", Storage="_Resource", ThisKey="ResourceId", OtherKey="ResourceId", IsForeignKey=true, DeleteOnNull=true)]
 		public Resource Resource
 		{
@@ -380,10 +418,10 @@
 					{
 						this._Resource.Entity = null;
-						previousValue.AssignedResources.Remove(this);
+						previousValue.AssignedProjectResources.Remove(this);
 					}
 					this._Resource.Entity = value;
 					if ((value != null))
 					{
-						value.AssignedResources.Add(this);
+						value.AssignedProjectResources.Add(this);
 						this._ResourceId = value.ResourceId;
 					}
@@ -397,28 +435,28 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Task_AssignedResource", Storage="_Job", ThisKey="TaskId", OtherKey="TaskId", IsForeignKey=true, DeleteRule="CASCADE")]
-		public Task Task
-		{
-			get
-			{
-				return this._Job.Entity;
-			}
-			set
-			{
-				Task previousValue = this._Job.Entity;
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_AssignedProjectResource", Storage="_Project", ThisKey="ProjectId", OtherKey="ProjectId", IsForeignKey=true, DeleteOnNull=true)]
+		public Project Project
+		{
+			get
+			{
+				return this._Project.Entity;
+			}
+			set
+			{
+				Project previousValue = this._Project.Entity;
 				if (((previousValue != value) 
-							|| (this._Job.HasLoadedOrAssignedValue == false)))
+							|| (this._Project.HasLoadedOrAssignedValue == false)))
 				{
 					this.SendPropertyChanging();
 					if ((previousValue != null))
 					{
-						this._Job.Entity = null;
-						previousValue.AssignedResources.Remove(this);
-					}
-					this._Job.Entity = value;
+						this._Project.Entity = null;
+						previousValue.AssignedProjectResources.Remove(this);
+					}
+					this._Project.Entity = value;
 					if ((value != null))
 					{
-						value.AssignedResources.Add(this);
-						this._JobId = value.TaskId;
+						value.AssignedProjectResources.Add(this);
+						this._JobId = value.ProjectId;
 					}
 					else
@@ -426,5 +464,5 @@
 						this._JobId = default(System.Guid);
 					}
-					this.SendPropertyChanged("Task");
+					this.SendPropertyChanged("Project");
 				}
 			}
@@ -903,5 +941,5 @@
 		private System.Nullable<System.Guid> _OwnerUserId;
 		
-		private EntitySet<AssignedResource> _AssignedResources;
+		private EntitySet<AssignedProjectResource> _AssignedProjectResources;
 		
 		private EntitySet<Resource> _ChildResources;
@@ -911,5 +949,5 @@
 		private EntitySet<StateLog> _StateLogs;
 		
-		private EntitySet<ResourcePermission> _ResourcePermissions;
+		private EntitySet<AssignedJobResource> _AssignedJobResources;
 		
 		private EntityRef<Resource> _ParentResource;
@@ -935,9 +973,9 @@
 		public Resource()
 		{
-			this._AssignedResources = new EntitySet<AssignedResource>(new Action<AssignedResource>(this.attach_AssignedResources), new Action<AssignedResource>(this.detach_AssignedResources));
+			this._AssignedProjectResources = new EntitySet<AssignedProjectResource>(new Action<AssignedProjectResource>(this.attach_AssignedProjectResources), new Action<AssignedProjectResource>(this.detach_AssignedProjectResources));
 			this._ChildResources = new EntitySet<Resource>(new Action<Resource>(this.attach_ChildResources), new Action<Resource>(this.detach_ChildResources));
 			this._UptimeCalendars = new EntitySet<Downtime>(new Action<Downtime>(this.attach_UptimeCalendars), new Action<Downtime>(this.detach_UptimeCalendars));
 			this._StateLogs = new EntitySet<StateLog>(new Action<StateLog>(this.attach_StateLogs), new Action<StateLog>(this.detach_StateLogs));
-			this._ResourcePermissions = new EntitySet<ResourcePermission>(new Action<ResourcePermission>(this.attach_ResourcePermissions), new Action<ResourcePermission>(this.detach_ResourcePermissions));
+			this._AssignedJobResources = new EntitySet<AssignedJobResource>(new Action<AssignedJobResource>(this.attach_AssignedJobResources), new Action<AssignedJobResource>(this.detach_AssignedJobResources));
 			this._ParentResource = default(EntityRef<Resource>);
 			OnCreated();
@@ -1068,14 +1106,14 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_AssignedResource", Storage="_AssignedResources", ThisKey="ResourceId", OtherKey="ResourceId")]
-		public EntitySet<AssignedResource> AssignedResources
-		{
-			get
-			{
-				return this._AssignedResources;
-			}
-			set
-			{
-				this._AssignedResources.Assign(value);
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_AssignedProjectResource", Storage="_AssignedProjectResources", ThisKey="ResourceId", OtherKey="ResourceId")]
+		public EntitySet<AssignedProjectResource> AssignedProjectResources
+		{
+			get
+			{
+				return this._AssignedProjectResources;
+			}
+			set
+			{
+				this._AssignedProjectResources.Assign(value);
 			}
 		}
@@ -1120,14 +1158,14 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_ResourcePermission", Storage="_ResourcePermissions", ThisKey="ResourceId", OtherKey="ResourceId")]
-		public EntitySet<ResourcePermission> ResourcePermissions
-		{
-			get
-			{
-				return this._ResourcePermissions;
-			}
-			set
-			{
-				this._ResourcePermissions.Assign(value);
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_AssignedJobResource", Storage="_AssignedJobResources", ThisKey="ResourceId", OtherKey="ResourceId")]
+		public EntitySet<AssignedJobResource> AssignedJobResources
+		{
+			get
+			{
+				return this._AssignedJobResources;
+			}
+			set
+			{
+				this._AssignedJobResources.Assign(value);
 			}
 		}
@@ -1187,5 +1225,5 @@
 		}
 		
-		private void attach_AssignedResources(AssignedResource entity)
+		private void attach_AssignedProjectResources(AssignedProjectResource entity)
 		{
 			this.SendPropertyChanging();
@@ -1193,5 +1231,5 @@
 		}
 		
-		private void detach_AssignedResources(AssignedResource entity)
+		private void detach_AssignedProjectResources(AssignedProjectResource entity)
 		{
 			this.SendPropertyChanging();
@@ -1235,5 +1273,5 @@
 		}
 		
-		private void attach_ResourcePermissions(ResourcePermission entity)
+		private void attach_AssignedJobResources(AssignedJobResource entity)
 		{
 			this.SendPropertyChanging();
@@ -1241,5 +1279,5 @@
 		}
 		
-		private void detach_ResourcePermissions(ResourcePermission entity)
+		private void detach_AssignedJobResources(AssignedJobResource entity)
 		{
 			this.SendPropertyChanging();
@@ -1620,6 +1658,4 @@
 		private System.Guid _HiveExperimentId;
 		
-		private EntitySet<AssignedResource> _AssignedResources;
-		
 		private EntitySet<RequiredPlugin> _RequiredPlugins;
 		
@@ -1666,5 +1702,4 @@
 		public Task()
 		{
-			this._AssignedResources = new EntitySet<AssignedResource>(new Action<AssignedResource>(this.attach_AssignedResources), new Action<AssignedResource>(this.detach_AssignedResources));
 			this._RequiredPlugins = new EntitySet<RequiredPlugin>(new Action<RequiredPlugin>(this.attach_RequiredPlugins), new Action<RequiredPlugin>(this.detach_RequiredPlugins));
 			this._Jobs = new EntitySet<Task>(new Action<Task>(this.attach_Jobs), new Action<Task>(this.detach_Jobs));
@@ -1924,17 +1959,4 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Task_AssignedResource", Storage="_AssignedResources", ThisKey="TaskId", OtherKey="TaskId")]
-		public EntitySet<AssignedResource> AssignedResources
-		{
-			get
-			{
-				return this._AssignedResources;
-			}
-			set
-			{
-				this._AssignedResources.Assign(value);
-			}
-		}
-		
 		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Task_RequiredPlugin", Storage="_RequiredPlugins", ThisKey="TaskId", OtherKey="TaskId")]
 		public EntitySet<RequiredPlugin> RequiredPlugins
@@ -2091,16 +2113,4 @@
 				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 			}
-		}
-		
-		private void attach_AssignedResources(AssignedResource entity)
-		{
-			this.SendPropertyChanging();
-			entity.Task = this;
-		}
-		
-		private void detach_AssignedResources(AssignedResource entity)
-		{
-			this.SendPropertyChanging();
-			entity.Task = null;
 		}
 		
@@ -2425,13 +2435,19 @@
 		private string _Description;
 		
-		private string _ResourceIds;
-		
 		private System.Guid _UserId;
 		
 		private System.DateTime _DateCreated;
 		
+		private System.Guid _ProjectId;
+		
+		private global::HeuristicLab.Services.Hive.DataAccess.JobState _State;
+		
 		private EntitySet<Task> _Jobs;
 		
 		private EntitySet<JobPermission> _HiveExperimentPermissions;
+		
+		private EntitySet<AssignedJobResource> _AssignedJobResources;
+		
+		private EntityRef<Project> _Project;
 		
     #region Extensibility Method Definitions
@@ -2445,10 +2461,12 @@
     partial void OnDescriptionChanging(string value);
     partial void OnDescriptionChanged();
-    partial void OnResourceIdsChanging(string value);
-    partial void OnResourceIdsChanged();
     partial void OnOwnerUserIdChanging(System.Guid value);
     partial void OnOwnerUserIdChanged();
     partial void OnDateCreatedChanging(System.DateTime value);
     partial void OnDateCreatedChanged();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
+    partial void OnStateChanging(global::HeuristicLab.Services.Hive.DataAccess.JobState value);
+    partial void OnStateChanged();
     #endregion
 		
@@ -2457,4 +2475,6 @@
 			this._Jobs = new EntitySet<Task>(new Action<Task>(this.attach_Jobs), new Action<Task>(this.detach_Jobs));
 			this._HiveExperimentPermissions = new EntitySet<JobPermission>(new Action<JobPermission>(this.attach_HiveExperimentPermissions), new Action<JobPermission>(this.detach_HiveExperimentPermissions));
+			this._AssignedJobResources = new EntitySet<AssignedJobResource>(new Action<AssignedJobResource>(this.attach_AssignedJobResources), new Action<AssignedJobResource>(this.detach_AssignedJobResources));
+			this._Project = default(EntityRef<Project>);
 			OnCreated();
 		}
@@ -2520,24 +2540,4 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ResourceIds", DbType="VarChar(MAX)")]
-		public string ResourceIds
-		{
-			get
-			{
-				return this._ResourceIds;
-			}
-			set
-			{
-				if ((this._ResourceIds != value))
-				{
-					this.OnResourceIdsChanging(value);
-					this.SendPropertyChanging();
-					this._ResourceIds = value;
-					this.SendPropertyChanged("ResourceIds");
-					this.OnResourceIdsChanged();
-				}
-			}
-		}
-		
 		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_UserId", DbType="UniqueIdentifier")]
 		public System.Guid OwnerUserId
@@ -2580,4 +2580,48 @@
 		}
 		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ProjectId", DbType="UniqueIdentifier NOT NULL")]
+		public System.Guid ProjectId
+		{
+			get
+			{
+				return this._ProjectId;
+			}
+			set
+			{
+				if ((this._ProjectId != value))
+				{
+					if (this._Project.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProjectId = value;
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Name="JobState", Storage="_State", DbType="VarChar(30)", CanBeNull=false)]
+		public global::HeuristicLab.Services.Hive.DataAccess.JobState State
+		{
+			get
+			{
+				return this._State;
+			}
+			set
+			{
+				if ((this._State != value))
+				{
+					this.OnStateChanging(value);
+					this.SendPropertyChanging();
+					this._State = value;
+					this.SendPropertyChanged("State");
+					this.OnStateChanged();
+				}
+			}
+		}
+		
 		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Job_Task", Storage="_Jobs", ThisKey="JobId", OtherKey="JobId")]
 		public EntitySet<Task> Tasks
@@ -2606,4 +2650,51 @@
 		}
 		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Job_AssignedJobResource", Storage="_AssignedJobResources", ThisKey="JobId", OtherKey="JobId")]
+		public EntitySet<AssignedJobResource> AssignedJobResources
+		{
+			get
+			{
+				return this._AssignedJobResources;
+			}
+			set
+			{
+				this._AssignedJobResources.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_Job", Storage="_Project", ThisKey="ProjectId", OtherKey="ProjectId", IsForeignKey=true)]
+		public Project Project
+		{
+			get
+			{
+				return this._Project.Entity;
+			}
+			set
+			{
+				Project previousValue = this._Project.Entity;
+				if (((previousValue != value) 
+							|| (this._Project.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._Project.Entity = null;
+						previousValue.Jobs.Remove(this);
+					}
+					this._Project.Entity = value;
+					if ((value != null))
+					{
+						value.Jobs.Add(this);
+						this._ProjectId = value.ProjectId;
+					}
+					else
+					{
+						this._ProjectId = default(System.Guid);
+					}
+					this.SendPropertyChanged("Project");
+				}
+			}
+		}
+		
 		public event PropertyChangingEventHandler PropertyChanging;
 		
@@ -2645,4 +2736,16 @@
 		
 		private void detach_HiveExperimentPermissions(JobPermission entity)
+		{
+			this.SendPropertyChanging();
+			entity.Job = null;
+		}
+		
+		private void attach_AssignedJobResources(AssignedJobResource entity)
+		{
+			this.SendPropertyChanging();
+			entity.Job = this;
+		}
+		
+		private void detach_AssignedJobResources(AssignedJobResource entity)
 		{
 			this.SendPropertyChanging();
@@ -2747,5 +2850,5 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Task_TaskData", Storage="_Job", ThisKey="TaskId", OtherKey="TaskId", IsForeignKey=true)]
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Task_TaskData", Storage="_Job", ThisKey="TaskId", OtherKey="TaskId", IsForeignKey=true, DeleteOnNull=true)]
 		public Task Task
 		{
@@ -3385,5 +3488,5 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Job_JobPermission", Storage="_HiveExperiment", ThisKey="JobId", OtherKey="JobId", IsForeignKey=true)]
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Job_JobPermission", Storage="_HiveExperiment", ThisKey="JobId", OtherKey="JobId", IsForeignKey=true, DeleteOnNull=true)]
 		public Job Job
 		{
@@ -3526,155 +3629,4 @@
 	}
 	
-	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.ResourcePermission")]
-	public partial class ResourcePermission : INotifyPropertyChanging, INotifyPropertyChanged
-	{
-		
-		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
-		
-		private System.Guid _ResourceId;
-		
-		private System.Guid _GrantedUserId;
-		
-		private System.Guid _GrantedByUserId;
-		
-		private EntityRef<Resource> _Resource;
-		
-    #region Extensibility Method Definitions
-    partial void OnLoaded();
-    partial void OnValidate(System.Data.Linq.ChangeAction action);
-    partial void OnCreated();
-    partial void OnResourceIdChanging(System.Guid value);
-    partial void OnResourceIdChanged();
-    partial void OnGrantedUserIdChanging(System.Guid value);
-    partial void OnGrantedUserIdChanged();
-    partial void OnGrantedByUserIdChanging(System.Guid value);
-    partial void OnGrantedByUserIdChanged();
-    #endregion
-		
-		public ResourcePermission()
-		{
-			this._Resource = default(EntityRef<Resource>);
-			OnCreated();
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ResourceId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
-		public System.Guid ResourceId
-		{
-			get
-			{
-				return this._ResourceId;
-			}
-			set
-			{
-				if ((this._ResourceId != value))
-				{
-					if (this._Resource.HasLoadedOrAssignedValue)
-					{
-						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
-					}
-					this.OnResourceIdChanging(value);
-					this.SendPropertyChanging();
-					this._ResourceId = value;
-					this.SendPropertyChanged("ResourceId");
-					this.OnResourceIdChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_GrantedUserId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
-		public System.Guid GrantedUserId
-		{
-			get
-			{
-				return this._GrantedUserId;
-			}
-			set
-			{
-				if ((this._GrantedUserId != value))
-				{
-					this.OnGrantedUserIdChanging(value);
-					this.SendPropertyChanging();
-					this._GrantedUserId = value;
-					this.SendPropertyChanged("GrantedUserId");
-					this.OnGrantedUserIdChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_GrantedByUserId", DbType="UniqueIdentifier NOT NULL")]
-		public System.Guid GrantedByUserId
-		{
-			get
-			{
-				return this._GrantedByUserId;
-			}
-			set
-			{
-				if ((this._GrantedByUserId != value))
-				{
-					this.OnGrantedByUserIdChanging(value);
-					this.SendPropertyChanging();
-					this._GrantedByUserId = value;
-					this.SendPropertyChanged("GrantedByUserId");
-					this.OnGrantedByUserIdChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_ResourcePermission", Storage="_Resource", ThisKey="ResourceId", OtherKey="ResourceId", IsForeignKey=true)]
-		public Resource Resource
-		{
-			get
-			{
-				return this._Resource.Entity;
-			}
-			set
-			{
-				Resource previousValue = this._Resource.Entity;
-				if (((previousValue != value) 
-							|| (this._Resource.HasLoadedOrAssignedValue == false)))
-				{
-					this.SendPropertyChanging();
-					if ((previousValue != null))
-					{
-						this._Resource.Entity = null;
-						previousValue.ResourcePermissions.Remove(this);
-					}
-					this._Resource.Entity = value;
-					if ((value != null))
-					{
-						value.ResourcePermissions.Add(this);
-						this._ResourceId = value.ResourceId;
-					}
-					else
-					{
-						this._ResourceId = default(System.Guid);
-					}
-					this.SendPropertyChanged("Resource");
-				}
-			}
-		}
-		
-		public event PropertyChangingEventHandler PropertyChanging;
-		
-		public event PropertyChangedEventHandler PropertyChanged;
-		
-		protected virtual void SendPropertyChanging()
-		{
-			if ((this.PropertyChanging != null))
-			{
-				this.PropertyChanging(this, emptyChangingEventArgs);
-			}
-		}
-		
-		protected virtual void SendPropertyChanged(String propertyName)
-		{
-			if ((this.PropertyChanged != null))
-			{
-				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
-			}
-		}
-	}
-	
 	[global::System.Data.Linq.Mapping.TableAttribute(Name="")]
 	public partial class UserPriority : INotifyPropertyChanging, INotifyPropertyChanged
@@ -3775,13 +3727,11 @@
 		private System.Guid _ResourceId;
 		
-		private System.Nullable<System.DateTime> _ExpirationTime;
-		
-		private System.Nullable<System.Guid> _ResourceGroupId;
-		
-		private System.Nullable<System.Guid> _ResourceGroup2Id;
-		
-		private string _GroupName;
-		
-		private string _GroupName2;
+		private System.Nullable<System.Guid> _ParentResourceId;
+		
+		private string _ResourceType;
+		
+		private System.DateTime _DateCreated;
+		
+		private System.Nullable<System.DateTime> _DateExpired;
 		
 		private EntitySet<FactTask> _FactTasks;
@@ -3799,14 +3749,12 @@
     partial void OnResourceIdChanging(System.Guid value);
     partial void OnResourceIdChanged();
-    partial void OnExpirationTimeChanging(System.Nullable<System.DateTime> value);
-    partial void OnExpirationTimeChanged();
-    partial void OnResourceGroupIdChanging(System.Nullable<System.Guid> value);
-    partial void OnResourceGroupIdChanged();
-    partial void OnResourceGroup2IdChanging(System.Nullable<System.Guid> value);
-    partial void OnResourceGroup2IdChanged();
-    partial void OnGroupNameChanging(string value);
-    partial void OnGroupNameChanged();
-    partial void OnGroupName2Changing(string value);
-    partial void OnGroupName2Changed();
+    partial void OnParentResourceIdChanging(System.Nullable<System.Guid> value);
+    partial void OnParentResourceIdChanged();
+    partial void OnResourceTypeChanging(string value);
+    partial void OnResourceTypeChanged();
+    partial void OnDateCreatedChanging(System.DateTime value);
+    partial void OnDateCreatedChanged();
+    partial void OnDateExpiredChanging(System.Nullable<System.DateTime> value);
+    partial void OnDateExpiredChanged();
     #endregion
 		
@@ -3878,100 +3826,80 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ExpirationTime", DbType="DateTime")]
-		public System.Nullable<System.DateTime> ExpirationTime
-		{
-			get
-			{
-				return this._ExpirationTime;
-			}
-			set
-			{
-				if ((this._ExpirationTime != value))
-				{
-					this.OnExpirationTimeChanging(value);
-					this.SendPropertyChanging();
-					this._ExpirationTime = value;
-					this.SendPropertyChanged("ExpirationTime");
-					this.OnExpirationTimeChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ResourceGroupId", DbType="UniqueIdentifier")]
-		public System.Nullable<System.Guid> ResourceGroupId
-		{
-			get
-			{
-				return this._ResourceGroupId;
-			}
-			set
-			{
-				if ((this._ResourceGroupId != value))
-				{
-					this.OnResourceGroupIdChanging(value);
-					this.SendPropertyChanging();
-					this._ResourceGroupId = value;
-					this.SendPropertyChanged("ResourceGroupId");
-					this.OnResourceGroupIdChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ResourceGroup2Id", DbType="UniqueIdentifier")]
-		public System.Nullable<System.Guid> ResourceGroup2Id
-		{
-			get
-			{
-				return this._ResourceGroup2Id;
-			}
-			set
-			{
-				if ((this._ResourceGroup2Id != value))
-				{
-					this.OnResourceGroup2IdChanging(value);
-					this.SendPropertyChanging();
-					this._ResourceGroup2Id = value;
-					this.SendPropertyChanged("ResourceGroup2Id");
-					this.OnResourceGroup2IdChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_GroupName")]
-		public string GroupName
-		{
-			get
-			{
-				return this._GroupName;
-			}
-			set
-			{
-				if ((this._GroupName != value))
-				{
-					this.OnGroupNameChanging(value);
-					this.SendPropertyChanging();
-					this._GroupName = value;
-					this.SendPropertyChanged("GroupName");
-					this.OnGroupNameChanged();
-				}
-			}
-		}
-		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_GroupName2")]
-		public string GroupName2
-		{
-			get
-			{
-				return this._GroupName2;
-			}
-			set
-			{
-				if ((this._GroupName2 != value))
-				{
-					this.OnGroupName2Changing(value);
-					this.SendPropertyChanging();
-					this._GroupName2 = value;
-					this.SendPropertyChanged("GroupName2");
-					this.OnGroupName2Changed();
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ParentResourceId", DbType="UniqueIdentifier")]
+		public System.Nullable<System.Guid> ParentResourceId
+		{
+			get
+			{
+				return this._ParentResourceId;
+			}
+			set
+			{
+				if ((this._ParentResourceId != value))
+				{
+					this.OnParentResourceIdChanging(value);
+					this.SendPropertyChanging();
+					this._ParentResourceId = value;
+					this.SendPropertyChanged("ParentResourceId");
+					this.OnParentResourceIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ResourceType", CanBeNull=false)]
+		public string ResourceType
+		{
+			get
+			{
+				return this._ResourceType;
+			}
+			set
+			{
+				if ((this._ResourceType != value))
+				{
+					this.OnResourceTypeChanging(value);
+					this.SendPropertyChanging();
+					this._ResourceType = value;
+					this.SendPropertyChanged("ResourceType");
+					this.OnResourceTypeChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DateCreated", DbType="DateTime NOT NULL")]
+		public System.DateTime DateCreated
+		{
+			get
+			{
+				return this._DateCreated;
+			}
+			set
+			{
+				if ((this._DateCreated != value))
+				{
+					this.OnDateCreatedChanging(value);
+					this.SendPropertyChanging();
+					this._DateCreated = value;
+					this.SendPropertyChanged("DateCreated");
+					this.OnDateCreatedChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DateExpired", DbType="DateTime")]
+		public System.Nullable<System.DateTime> DateExpired
+		{
+			get
+			{
+				return this._DateExpired;
+			}
+			set
+			{
+				if ((this._DateExpired != value))
+				{
+					this.OnDateExpiredChanging(value);
+					this.SendPropertyChanging();
+					this._DateExpired = value;
+					this.SendPropertyChanged("DateExpired");
+					this.OnDateExpiredChanged();
 				}
 			}
@@ -4575,5 +4503,9 @@
 		private System.Nullable<System.DateTime> _DateCompleted;
 		
+		private System.Guid _ProjectId;
+		
 		private EntitySet<FactTask> _FactTasks;
+		
+		private EntityRef<DimProject> _DimProject;
 		
     #region Extensibility Method Definitions
@@ -4597,4 +4529,6 @@
     partial void OnDateCompletedChanging(System.Nullable<System.DateTime> value);
     partial void OnDateCompletedChanged();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
     #endregion
 		
@@ -4602,4 +4536,5 @@
 		{
 			this._FactTasks = new EntitySet<FactTask>(new Action<FactTask>(this.attach_FactTasks), new Action<FactTask>(this.detach_FactTasks));
+			this._DimProject = default(EntityRef<DimProject>);
 			OnCreated();
 		}
@@ -4765,4 +4700,28 @@
 		}
 		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ProjectId", DbType="UniqueIdentifier NOT NULL")]
+		public System.Guid ProjectId
+		{
+			get
+			{
+				return this._ProjectId;
+			}
+			set
+			{
+				if ((this._ProjectId != value))
+				{
+					if (this._DimProject.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProjectId = value;
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
 		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimJob_FactTask", Storage="_FactTasks", ThisKey="JobId", OtherKey="JobId")]
 		public EntitySet<FactTask> FactTasks
@@ -4775,4 +4734,38 @@
 			{
 				this._FactTasks.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimProject_DimJob", Storage="_DimProject", ThisKey="ProjectId", OtherKey="Id", IsForeignKey=true)]
+		public DimProject DimProject
+		{
+			get
+			{
+				return this._DimProject.Entity;
+			}
+			set
+			{
+				DimProject previousValue = this._DimProject.Entity;
+				if (((previousValue != value) 
+							|| (this._DimProject.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._DimProject.Entity = null;
+						previousValue.DimJobs.Remove(this);
+					}
+					this._DimProject.Entity = value;
+					if ((value != null))
+					{
+						value.DimJobs.Add(this);
+						this._ProjectId = value.Id;
+					}
+					else
+					{
+						this._ProjectId = default(System.Guid);
+					}
+					this.SendPropertyChanged("DimProject");
+				}
 			}
 		}
@@ -4830,4 +4823,6 @@
 		
 		private EntitySet<FactClientInfo> _FactClientInfos;
+		
+		private EntitySet<FactProjectInfo> _FactProjectInfos;
 		
     #region Extensibility Method Definitions
@@ -4852,4 +4847,5 @@
 		{
 			this._FactClientInfos = new EntitySet<FactClientInfo>(new Action<FactClientInfo>(this.attach_FactClientInfos), new Action<FactClientInfo>(this.detach_FactClientInfos));
+			this._FactProjectInfos = new EntitySet<FactProjectInfo>(new Action<FactProjectInfo>(this.attach_FactProjectInfos), new Action<FactProjectInfo>(this.detach_FactProjectInfos));
 			OnCreated();
 		}
@@ -4988,4 +4984,17 @@
 		}
 		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimTime_FactProjectInfo", Storage="_FactProjectInfos", ThisKey="Time", OtherKey="Time")]
+		public EntitySet<FactProjectInfo> FactProjectInfos
+		{
+			get
+			{
+				return this._FactProjectInfos;
+			}
+			set
+			{
+				this._FactProjectInfos.Assign(value);
+			}
+		}
+		
 		public event PropertyChangingEventHandler PropertyChanging;
 		
@@ -5015,4 +5024,16 @@
 		
 		private void detach_FactClientInfos(FactClientInfo entity)
+		{
+			this.SendPropertyChanging();
+			entity.DimTime = null;
+		}
+		
+		private void attach_FactProjectInfos(FactProjectInfo entity)
+		{
+			this.SendPropertyChanging();
+			entity.DimTime = this;
+		}
+		
+		private void detach_FactProjectInfos(FactProjectInfo entity)
 		{
 			this.SendPropertyChanging();
@@ -5261,5 +5282,5 @@
 		}
 		
-		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_UserId", DbType="UniqueIdentifier NULL", IsPrimaryKey=true)]
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_UserId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
 		public System.Guid UserId
 		{
@@ -5607,4 +5628,1304 @@
 		}
 	}
+	
+	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Project")]
+	public partial class Project : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private System.Guid _ProjectId;
+		
+		private System.Nullable<System.Guid> _ParentProjectId;
+		
+		private System.DateTime _DateCreated;
+		
+		private string _Name;
+		
+		private string _Description;
+		
+		private System.Guid _OwnerUserId;
+		
+		private System.DateTime _StartDate;
+		
+		private System.Nullable<System.DateTime> _EndDate;
+		
+		private EntitySet<AssignedProjectResource> _AssignedProjectResources;
+		
+		private EntitySet<Job> _Jobs;
+		
+		private EntitySet<Project> _Projects;
+		
+		private EntitySet<ProjectPermission> _ProjectPermissions;
+		
+		private EntityRef<Project> _Project1;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
+    partial void OnParentProjectIdChanging(System.Nullable<System.Guid> value);
+    partial void OnParentProjectIdChanged();
+    partial void OnDateCreatedChanging(System.DateTime value);
+    partial void OnDateCreatedChanged();
+    partial void OnNameChanging(string value);
+    partial void OnNameChanged();
+    partial void OnDescriptionChanging(string value);
+    partial void OnDescriptionChanged();
+    partial void OnOwnerUserIdChanging(System.Guid value);
+    partial void OnOwnerUserIdChanged();
+    partial void OnStartDateChanging(System.DateTime value);
+    partial void OnStartDateChanged();
+    partial void OnEndDateChanging(System.Nullable<System.DateTime> value);
+    partial void OnEndDateChanged();
+    #endregion
+		
+		public Project()
+		{
+			this._AssignedProjectResources = new EntitySet<AssignedProjectResource>(new Action<AssignedProjectResource>(this.attach_AssignedProjectResources), new Action<AssignedProjectResource>(this.detach_AssignedProjectResources));
+			this._Jobs = new EntitySet<Job>(new Action<Job>(this.attach_Jobs), new Action<Job>(this.detach_Jobs));
+			this._Projects = new EntitySet<Project>(new Action<Project>(this.attach_Projects), new Action<Project>(this.detach_Projects));
+			this._ProjectPermissions = new EntitySet<ProjectPermission>(new Action<ProjectPermission>(this.attach_ProjectPermissions), new Action<ProjectPermission>(this.detach_ProjectPermissions));
+			this._Project1 = default(EntityRef<Project>);
+			OnCreated();
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ProjectId", AutoSync=AutoSync.OnInsert, DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true, IsDbGenerated=true)]
+		public System.Guid ProjectId
+		{
+			get
+			{
+				return this._ProjectId;
+			}
+			set
+			{
+				if ((this._ProjectId != value))
+				{
+					this.OnProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProjectId = value;
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ParentProjectId", DbType="UniqueIdentifier")]
+		public System.Nullable<System.Guid> ParentProjectId
+		{
+			get
+			{
+				return this._ParentProjectId;
+			}
+			set
+			{
+				if ((this._ParentProjectId != value))
+				{
+					if (this._Project1.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnParentProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ParentProjectId = value;
+					this.SendPropertyChanged("ParentProjectId");
+					this.OnParentProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DateCreated", DbType="DateTime NOT NULL")]
+		public System.DateTime DateCreated
+		{
+			get
+			{
+				return this._DateCreated;
+			}
+			set
+			{
+				if ((this._DateCreated != value))
+				{
+					this.OnDateCreatedChanging(value);
+					this.SendPropertyChanging();
+					this._DateCreated = value;
+					this.SendPropertyChanged("DateCreated");
+					this.OnDateCreatedChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", DbType="VarChar(MAX) NOT NULL", CanBeNull=false)]
+		public string Name
+		{
+			get
+			{
+				return this._Name;
+			}
+			set
+			{
+				if ((this._Name != value))
+				{
+					this.OnNameChanging(value);
+					this.SendPropertyChanging();
+					this._Name = value;
+					this.SendPropertyChanged("Name");
+					this.OnNameChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Description", DbType="VarChar(MAX)")]
+		public string Description
+		{
+			get
+			{
+				return this._Description;
+			}
+			set
+			{
+				if ((this._Description != value))
+				{
+					this.OnDescriptionChanging(value);
+					this.SendPropertyChanging();
+					this._Description = value;
+					this.SendPropertyChanged("Description");
+					this.OnDescriptionChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_OwnerUserId", DbType="UniqueIdentifier NOT NULL")]
+		public System.Guid OwnerUserId
+		{
+			get
+			{
+				return this._OwnerUserId;
+			}
+			set
+			{
+				if ((this._OwnerUserId != value))
+				{
+					this.OnOwnerUserIdChanging(value);
+					this.SendPropertyChanging();
+					this._OwnerUserId = value;
+					this.SendPropertyChanged("OwnerUserId");
+					this.OnOwnerUserIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_StartDate", DbType="DateTime NOT NULL")]
+		public System.DateTime StartDate
+		{
+			get
+			{
+				return this._StartDate;
+			}
+			set
+			{
+				if ((this._StartDate != value))
+				{
+					this.OnStartDateChanging(value);
+					this.SendPropertyChanging();
+					this._StartDate = value;
+					this.SendPropertyChanged("StartDate");
+					this.OnStartDateChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_EndDate", DbType="DateTime")]
+		public System.Nullable<System.DateTime> EndDate
+		{
+			get
+			{
+				return this._EndDate;
+			}
+			set
+			{
+				if ((this._EndDate != value))
+				{
+					this.OnEndDateChanging(value);
+					this.SendPropertyChanging();
+					this._EndDate = value;
+					this.SendPropertyChanged("EndDate");
+					this.OnEndDateChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_AssignedProjectResource", Storage="_AssignedProjectResources", ThisKey="ProjectId", OtherKey="ProjectId")]
+		public EntitySet<AssignedProjectResource> AssignedProjectResources
+		{
+			get
+			{
+				return this._AssignedProjectResources;
+			}
+			set
+			{
+				this._AssignedProjectResources.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_Job", Storage="_Jobs", ThisKey="ProjectId", OtherKey="ProjectId")]
+		public EntitySet<Job> Jobs
+		{
+			get
+			{
+				return this._Jobs;
+			}
+			set
+			{
+				this._Jobs.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_Project", Storage="_Projects", ThisKey="ProjectId", OtherKey="ParentProjectId")]
+		public EntitySet<Project> ChildProjects
+		{
+			get
+			{
+				return this._Projects;
+			}
+			set
+			{
+				this._Projects.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_ProjectPermission", Storage="_ProjectPermissions", ThisKey="ProjectId", OtherKey="ProjectId")]
+		public EntitySet<ProjectPermission> ProjectPermissions
+		{
+			get
+			{
+				return this._ProjectPermissions;
+			}
+			set
+			{
+				this._ProjectPermissions.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_Project", Storage="_Project1", ThisKey="ParentProjectId", OtherKey="ProjectId", IsForeignKey=true)]
+		public Project ParentProject
+		{
+			get
+			{
+				return this._Project1.Entity;
+			}
+			set
+			{
+				Project previousValue = this._Project1.Entity;
+				if (((previousValue != value) 
+							|| (this._Project1.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._Project1.Entity = null;
+						previousValue.ChildProjects.Remove(this);
+					}
+					this._Project1.Entity = value;
+					if ((value != null))
+					{
+						value.ChildProjects.Add(this);
+						this._ParentProjectId = value.ProjectId;
+					}
+					else
+					{
+						this._ParentProjectId = default(Nullable<System.Guid>);
+					}
+					this.SendPropertyChanged("ParentProject");
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+		
+		private void attach_AssignedProjectResources(AssignedProjectResource entity)
+		{
+			this.SendPropertyChanging();
+			entity.Project = this;
+		}
+		
+		private void detach_AssignedProjectResources(AssignedProjectResource entity)
+		{
+			this.SendPropertyChanging();
+			entity.Project = null;
+		}
+		
+		private void attach_Jobs(Job entity)
+		{
+			this.SendPropertyChanging();
+			entity.Project = this;
+		}
+		
+		private void detach_Jobs(Job entity)
+		{
+			this.SendPropertyChanging();
+			entity.Project = null;
+		}
+		
+		private void attach_Projects(Project entity)
+		{
+			this.SendPropertyChanging();
+			entity.ParentProject = this;
+		}
+		
+		private void detach_Projects(Project entity)
+		{
+			this.SendPropertyChanging();
+			entity.ParentProject = null;
+		}
+		
+		private void attach_ProjectPermissions(ProjectPermission entity)
+		{
+			this.SendPropertyChanging();
+			entity.Project = this;
+		}
+		
+		private void detach_ProjectPermissions(ProjectPermission entity)
+		{
+			this.SendPropertyChanging();
+			entity.Project = null;
+		}
+	}
+	
+	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.ProjectPermission")]
+	public partial class ProjectPermission : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private System.Guid _ProjectId;
+		
+		private System.Guid _GrantedUserId;
+		
+		private System.Guid _GrantedByUserId;
+		
+		private EntityRef<Project> _Project;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
+    partial void OnGrantedUserIdChanging(System.Guid value);
+    partial void OnGrantedUserIdChanged();
+    partial void OnGrantedByUserIdChanging(System.Guid value);
+    partial void OnGrantedByUserIdChanged();
+    #endregion
+		
+		public ProjectPermission()
+		{
+			this._Project = default(EntityRef<Project>);
+			OnCreated();
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ProjectId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
+		public System.Guid ProjectId
+		{
+			get
+			{
+				return this._ProjectId;
+			}
+			set
+			{
+				if ((this._ProjectId != value))
+				{
+					if (this._Project.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProjectId = value;
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_GrantedUserId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
+		public System.Guid GrantedUserId
+		{
+			get
+			{
+				return this._GrantedUserId;
+			}
+			set
+			{
+				if ((this._GrantedUserId != value))
+				{
+					this.OnGrantedUserIdChanging(value);
+					this.SendPropertyChanging();
+					this._GrantedUserId = value;
+					this.SendPropertyChanged("GrantedUserId");
+					this.OnGrantedUserIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_GrantedByUserId", DbType="UniqueIdentifier NOT NULL")]
+		public System.Guid GrantedByUserId
+		{
+			get
+			{
+				return this._GrantedByUserId;
+			}
+			set
+			{
+				if ((this._GrantedByUserId != value))
+				{
+					this.OnGrantedByUserIdChanging(value);
+					this.SendPropertyChanging();
+					this._GrantedByUserId = value;
+					this.SendPropertyChanged("GrantedByUserId");
+					this.OnGrantedByUserIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Project_ProjectPermission", Storage="_Project", ThisKey="ProjectId", OtherKey="ProjectId", IsForeignKey=true, DeleteOnNull=true)]
+		public Project Project
+		{
+			get
+			{
+				return this._Project.Entity;
+			}
+			set
+			{
+				Project previousValue = this._Project.Entity;
+				if (((previousValue != value) 
+							|| (this._Project.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._Project.Entity = null;
+						previousValue.ProjectPermissions.Remove(this);
+					}
+					this._Project.Entity = value;
+					if ((value != null))
+					{
+						value.ProjectPermissions.Add(this);
+						this._ProjectId = value.ProjectId;
+					}
+					else
+					{
+						this._ProjectId = default(System.Guid);
+					}
+					this.SendPropertyChanged("Project");
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.AssignedJobResource")]
+	public partial class AssignedJobResource : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private System.Guid _ResourceId;
+		
+		private System.Guid _JobId;
+		
+		private EntityRef<Resource> _Resource;
+		
+		private EntityRef<Job> _Job;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnResourceIdChanging(System.Guid value);
+    partial void OnResourceIdChanged();
+    partial void OnJobIdChanging(System.Guid value);
+    partial void OnJobIdChanged();
+    #endregion
+		
+		public AssignedJobResource()
+		{
+			this._Resource = default(EntityRef<Resource>);
+			this._Job = default(EntityRef<Job>);
+			OnCreated();
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ResourceId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
+		public System.Guid ResourceId
+		{
+			get
+			{
+				return this._ResourceId;
+			}
+			set
+			{
+				if ((this._ResourceId != value))
+				{
+					if (this._Resource.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnResourceIdChanging(value);
+					this.SendPropertyChanging();
+					this._ResourceId = value;
+					this.SendPropertyChanged("ResourceId");
+					this.OnResourceIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_JobId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
+		public System.Guid JobId
+		{
+			get
+			{
+				return this._JobId;
+			}
+			set
+			{
+				if ((this._JobId != value))
+				{
+					if (this._Job.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnJobIdChanging(value);
+					this.SendPropertyChanging();
+					this._JobId = value;
+					this.SendPropertyChanged("JobId");
+					this.OnJobIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Resource_AssignedJobResource", Storage="_Resource", ThisKey="ResourceId", OtherKey="ResourceId", IsForeignKey=true, DeleteOnNull=true)]
+		public Resource Resource
+		{
+			get
+			{
+				return this._Resource.Entity;
+			}
+			set
+			{
+				Resource previousValue = this._Resource.Entity;
+				if (((previousValue != value) 
+							|| (this._Resource.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._Resource.Entity = null;
+						previousValue.AssignedJobResources.Remove(this);
+					}
+					this._Resource.Entity = value;
+					if ((value != null))
+					{
+						value.AssignedJobResources.Add(this);
+						this._ResourceId = value.ResourceId;
+					}
+					else
+					{
+						this._ResourceId = default(System.Guid);
+					}
+					this.SendPropertyChanged("Resource");
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Job_AssignedJobResource", Storage="_Job", ThisKey="JobId", OtherKey="JobId", IsForeignKey=true, DeleteOnNull=true)]
+		public Job Job
+		{
+			get
+			{
+				return this._Job.Entity;
+			}
+			set
+			{
+				Job previousValue = this._Job.Entity;
+				if (((previousValue != value) 
+							|| (this._Job.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._Job.Entity = null;
+						previousValue.AssignedJobResources.Remove(this);
+					}
+					this._Job.Entity = value;
+					if ((value != null))
+					{
+						value.AssignedJobResources.Add(this);
+						this._JobId = value.JobId;
+					}
+					else
+					{
+						this._JobId = default(System.Guid);
+					}
+					this.SendPropertyChanged("Job");
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[global::System.Data.Linq.Mapping.TableAttribute(Name="[statistics].FactProjectInfo")]
+	public partial class FactProjectInfo : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private System.Guid _ProjectId;
+		
+		private System.DateTime _Time;
+		
+		private int _NumTotalCores;
+		
+		private int _NumUsedCores;
+		
+		private int _TotalMemory;
+		
+		private int _UsedMemory;
+		
+		private EntityRef<DimProject> _DimProject;
+		
+		private EntityRef<DimTime> _DimTime;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
+    partial void OnTimeChanging(System.DateTime value);
+    partial void OnTimeChanged();
+    partial void OnNumTotalCoresChanging(int value);
+    partial void OnNumTotalCoresChanged();
+    partial void OnNumUsedCoresChanging(int value);
+    partial void OnNumUsedCoresChanged();
+    partial void OnTotalMemoryChanging(int value);
+    partial void OnTotalMemoryChanged();
+    partial void OnUsedMemoryChanging(int value);
+    partial void OnUsedMemoryChanged();
+    #endregion
+		
+		public FactProjectInfo()
+		{
+			this._DimProject = default(EntityRef<DimProject>);
+			this._DimTime = default(EntityRef<DimTime>);
+			OnCreated();
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ProjectId", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
+		public System.Guid ProjectId
+		{
+			get
+			{
+				return this._ProjectId;
+			}
+			set
+			{
+				if ((this._ProjectId != value))
+				{
+					if (this._DimProject.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProjectId = value;
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Time", DbType="DateTime NOT NULL", IsPrimaryKey=true)]
+		public System.DateTime Time
+		{
+			get
+			{
+				return this._Time;
+			}
+			set
+			{
+				if ((this._Time != value))
+				{
+					if (this._DimTime.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnTimeChanging(value);
+					this.SendPropertyChanging();
+					this._Time = value;
+					this.SendPropertyChanged("Time");
+					this.OnTimeChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_NumTotalCores", DbType="Int NOT NULL")]
+		public int NumTotalCores
+		{
+			get
+			{
+				return this._NumTotalCores;
+			}
+			set
+			{
+				if ((this._NumTotalCores != value))
+				{
+					this.OnNumTotalCoresChanging(value);
+					this.SendPropertyChanging();
+					this._NumTotalCores = value;
+					this.SendPropertyChanged("NumTotalCores");
+					this.OnNumTotalCoresChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_NumUsedCores", DbType="Int NOT NULL")]
+		public int NumUsedCores
+		{
+			get
+			{
+				return this._NumUsedCores;
+			}
+			set
+			{
+				if ((this._NumUsedCores != value))
+				{
+					this.OnNumUsedCoresChanging(value);
+					this.SendPropertyChanging();
+					this._NumUsedCores = value;
+					this.SendPropertyChanged("NumUsedCores");
+					this.OnNumUsedCoresChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_TotalMemory", DbType="Int NOT NULL")]
+		public int TotalMemory
+		{
+			get
+			{
+				return this._TotalMemory;
+			}
+			set
+			{
+				if ((this._TotalMemory != value))
+				{
+					this.OnTotalMemoryChanging(value);
+					this.SendPropertyChanging();
+					this._TotalMemory = value;
+					this.SendPropertyChanged("TotalMemory");
+					this.OnTotalMemoryChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_UsedMemory", DbType="Int NOT NULL")]
+		public int UsedMemory
+		{
+			get
+			{
+				return this._UsedMemory;
+			}
+			set
+			{
+				if ((this._UsedMemory != value))
+				{
+					this.OnUsedMemoryChanging(value);
+					this.SendPropertyChanging();
+					this._UsedMemory = value;
+					this.SendPropertyChanged("UsedMemory");
+					this.OnUsedMemoryChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimProject_FactProjectInfo", Storage="_DimProject", ThisKey="ProjectId", OtherKey="Id", IsForeignKey=true)]
+		public DimProject DimProject
+		{
+			get
+			{
+				return this._DimProject.Entity;
+			}
+			set
+			{
+				DimProject previousValue = this._DimProject.Entity;
+				if (((previousValue != value) 
+							|| (this._DimProject.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._DimProject.Entity = null;
+						previousValue.FactProjectInfos.Remove(this);
+					}
+					this._DimProject.Entity = value;
+					if ((value != null))
+					{
+						value.FactProjectInfos.Add(this);
+						this._ProjectId = value.Id;
+					}
+					else
+					{
+						this._ProjectId = default(System.Guid);
+					}
+					this.SendPropertyChanged("DimProject");
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimTime_FactProjectInfo", Storage="_DimTime", ThisKey="Time", OtherKey="Time", IsForeignKey=true)]
+		public DimTime DimTime
+		{
+			get
+			{
+				return this._DimTime.Entity;
+			}
+			set
+			{
+				DimTime previousValue = this._DimTime.Entity;
+				if (((previousValue != value) 
+							|| (this._DimTime.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._DimTime.Entity = null;
+						previousValue.FactProjectInfos.Remove(this);
+					}
+					this._DimTime.Entity = value;
+					if ((value != null))
+					{
+						value.FactProjectInfos.Add(this);
+						this._Time = value.Time;
+					}
+					else
+					{
+						this._Time = default(System.DateTime);
+					}
+					this.SendPropertyChanged("DimTime");
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[global::System.Data.Linq.Mapping.TableAttribute(Name="[statistics].DimProject")]
+	public partial class DimProject : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private System.Guid _Id;
+		
+		private System.Guid _ProjectId;
+		
+		private System.Nullable<System.Guid> _ParentId;
+		
+		private string _Name;
+		
+		private string _Description;
+		
+		private System.Guid _OwnerUserId;
+		
+		private System.DateTime _StartDate;
+		
+		private System.Nullable<System.DateTime> _EndDate;
+		
+		private System.DateTime _DateCreated;
+		
+		private System.Nullable<System.DateTime> _DateExpired;
+		
+		private EntitySet<DimJob> _DimJobs;
+		
+		private EntitySet<FactProjectInfo> _FactProjectInfos;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnIdChanging(System.Guid value);
+    partial void OnIdChanged();
+    partial void OnProjectIdChanging(System.Guid value);
+    partial void OnProjectIdChanged();
+    partial void OnParentProjectIdChanging(System.Nullable<System.Guid> value);
+    partial void OnParentProjectIdChanged();
+    partial void OnNameChanging(string value);
+    partial void OnNameChanged();
+    partial void OnDescriptionChanging(string value);
+    partial void OnDescriptionChanged();
+    partial void OnOwnerUserIdChanging(System.Guid value);
+    partial void OnOwnerUserIdChanged();
+    partial void OnStartDateChanging(System.DateTime value);
+    partial void OnStartDateChanged();
+    partial void OnEndDateChanging(System.Nullable<System.DateTime> value);
+    partial void OnEndDateChanged();
+    partial void OnDateCreatedChanging(System.DateTime value);
+    partial void OnDateCreatedChanged();
+    partial void OnDateExpiredChanging(System.Nullable<System.DateTime> value);
+    partial void OnDateExpiredChanged();
+    #endregion
+		
+		public DimProject()
+		{
+			this._DimJobs = new EntitySet<DimJob>(new Action<DimJob>(this.attach_DimJobs), new Action<DimJob>(this.detach_DimJobs));
+			this._FactProjectInfos = new EntitySet<FactProjectInfo>(new Action<FactProjectInfo>(this.attach_FactProjectInfos), new Action<FactProjectInfo>(this.detach_FactProjectInfos));
+			OnCreated();
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true, IsDbGenerated=true)]
+		public System.Guid Id
+		{
+			get
+			{
+				return this._Id;
+			}
+			set
+			{
+				if ((this._Id != value))
+				{
+					this.OnIdChanging(value);
+					this.SendPropertyChanging();
+					this._Id = value;
+					this.SendPropertyChanged("Id");
+					this.OnIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ProjectId", DbType="UniqueIdentifier NOT NULL")]
+		public System.Guid ProjectId
+		{
+			get
+			{
+				return this._ProjectId;
+			}
+			set
+			{
+				if ((this._ProjectId != value))
+				{
+					this.OnProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProjectId = value;
+					this.SendPropertyChanged("ProjectId");
+					this.OnProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ParentId", DbType="UniqueIdentifier")]
+		public System.Nullable<System.Guid> ParentProjectId
+		{
+			get
+			{
+				return this._ParentId;
+			}
+			set
+			{
+				if ((this._ParentId != value))
+				{
+					this.OnParentProjectIdChanging(value);
+					this.SendPropertyChanging();
+					this._ParentId = value;
+					this.SendPropertyChanged("ParentProjectId");
+					this.OnParentProjectIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", CanBeNull=false)]
+		public string Name
+		{
+			get
+			{
+				return this._Name;
+			}
+			set
+			{
+				if ((this._Name != value))
+				{
+					this.OnNameChanging(value);
+					this.SendPropertyChanging();
+					this._Name = value;
+					this.SendPropertyChanged("Name");
+					this.OnNameChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Description")]
+		public string Description
+		{
+			get
+			{
+				return this._Description;
+			}
+			set
+			{
+				if ((this._Description != value))
+				{
+					this.OnDescriptionChanging(value);
+					this.SendPropertyChanging();
+					this._Description = value;
+					this.SendPropertyChanged("Description");
+					this.OnDescriptionChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_OwnerUserId", DbType="UniqueIdentifier NOT NULL")]
+		public System.Guid OwnerUserId
+		{
+			get
+			{
+				return this._OwnerUserId;
+			}
+			set
+			{
+				if ((this._OwnerUserId != value))
+				{
+					this.OnOwnerUserIdChanging(value);
+					this.SendPropertyChanging();
+					this._OwnerUserId = value;
+					this.SendPropertyChanged("OwnerUserId");
+					this.OnOwnerUserIdChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_StartDate", DbType="DateTime NOT NULL")]
+		public System.DateTime StartDate
+		{
+			get
+			{
+				return this._StartDate;
+			}
+			set
+			{
+				if ((this._StartDate != value))
+				{
+					this.OnStartDateChanging(value);
+					this.SendPropertyChanging();
+					this._StartDate = value;
+					this.SendPropertyChanged("StartDate");
+					this.OnStartDateChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_EndDate", DbType="DateTime")]
+		public System.Nullable<System.DateTime> EndDate
+		{
+			get
+			{
+				return this._EndDate;
+			}
+			set
+			{
+				if ((this._EndDate != value))
+				{
+					this.OnEndDateChanging(value);
+					this.SendPropertyChanging();
+					this._EndDate = value;
+					this.SendPropertyChanged("EndDate");
+					this.OnEndDateChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DateCreated", DbType="DateTime NOT NULL")]
+		public System.DateTime DateCreated
+		{
+			get
+			{
+				return this._DateCreated;
+			}
+			set
+			{
+				if ((this._DateCreated != value))
+				{
+					this.OnDateCreatedChanging(value);
+					this.SendPropertyChanging();
+					this._DateCreated = value;
+					this.SendPropertyChanged("DateCreated");
+					this.OnDateCreatedChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DateExpired", DbType="DateTime")]
+		public System.Nullable<System.DateTime> DateExpired
+		{
+			get
+			{
+				return this._DateExpired;
+			}
+			set
+			{
+				if ((this._DateExpired != value))
+				{
+					this.OnDateExpiredChanging(value);
+					this.SendPropertyChanging();
+					this._DateExpired = value;
+					this.SendPropertyChanged("DateExpired");
+					this.OnDateExpiredChanged();
+				}
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimProject_DimJob", Storage="_DimJobs", ThisKey="Id", OtherKey="ProjectId")]
+		public EntitySet<DimJob> DimJobs
+		{
+			get
+			{
+				return this._DimJobs;
+			}
+			set
+			{
+				this._DimJobs.Assign(value);
+			}
+		}
+		
+		[global::System.Data.Linq.Mapping.AssociationAttribute(Name="DimProject_FactProjectInfo", Storage="_FactProjectInfos", ThisKey="Id", OtherKey="ProjectId")]
+		public EntitySet<FactProjectInfo> FactProjectInfos
+		{
+			get
+			{
+				return this._FactProjectInfos;
+			}
+			set
+			{
+				this._FactProjectInfos.Assign(value);
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+		
+		private void attach_DimJobs(DimJob entity)
+		{
+			this.SendPropertyChanging();
+			entity.DimProject = this;
+		}
+		
+		private void detach_DimJobs(DimJob entity)
+		{
+			this.SendPropertyChanging();
+			entity.DimProject = null;
+		}
+		
+		private void attach_FactProjectInfos(FactProjectInfo entity)
+		{
+			this.SendPropertyChanging();
+			entity.DimProject = this;
+		}
+		
+		private void detach_FactProjectInfos(FactProjectInfo entity)
+		{
+			this.SendPropertyChanging();
+			entity.DimProject = null;
+		}
+	}
 }
 #pragma warning restore 1591
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Interfaces/IPersistenceManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Interfaces/IPersistenceManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Interfaces/IPersistenceManager.cs	(revision 16240)
@@ -31,5 +31,6 @@
 
     #region Hive daos
-    AssignedResourceDao AssignedResourceDao { get; }
+    AssignedJobResourceDao AssignedJobResourceDao { get; }
+    AssignedProjectResourceDao AssignedProjectResourceDao { get; }
     DowntimeDao DowntimeDao { get; }
     JobDao JobDao { get; }
@@ -38,7 +39,8 @@
     PluginDao PluginDao { get; }
     PluginDataDao PluginDataDao { get; }
+    ProjectDao ProjectDao { get; }
+    ProjectPermissionDao ProjectPermissionDao { get; }
     RequiredPluginDao RequiredPluginDao { get; }
     ResourceDao ResourceDao { get; }
-    ResourcePermissionDao ResourcePermissionDao { get; }
     SlaveDao SlaveDao { get; }
     SlaveGroupDao SlaveGroupDao { get; }
@@ -56,4 +58,6 @@
     FactClientInfoDao FactClientInfoDao { get; }
     FactTaskDao FactTaskDao { get; }
+    DimProjectDao DimProjectDao { get; }
+    FactProjectInfoDao FactProjectInfoDao { get; }
     #endregion
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Manager/PersistenceManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Manager/PersistenceManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Manager/PersistenceManager.cs	(revision 16240)
@@ -38,7 +38,12 @@
     #region Hive daos
 
-    private AssignedResourceDao assignedResourceDao;
-    public AssignedResourceDao AssignedResourceDao {
-      get { return assignedResourceDao ?? (assignedResourceDao = new AssignedResourceDao(dataContext)); }
+    private AssignedJobResourceDao assignedJobResourceDao;
+    public AssignedJobResourceDao AssignedJobResourceDao {
+      get { return assignedJobResourceDao ?? (assignedJobResourceDao = new AssignedJobResourceDao(dataContext)); }
+    }
+
+    private AssignedProjectResourceDao assignedProjectResourceDao;
+    public AssignedProjectResourceDao AssignedProjectResourceDao {
+      get { return assignedProjectResourceDao ?? (assignedProjectResourceDao = new AssignedProjectResourceDao(dataContext)); }
     }
 
@@ -73,4 +78,14 @@
     }
 
+    private ProjectDao projectDao;
+    public ProjectDao ProjectDao {
+      get { return projectDao ?? (projectDao = new ProjectDao(dataContext)); }
+    }
+
+    private ProjectPermissionDao projectPermissionDao;
+    public ProjectPermissionDao ProjectPermissionDao {
+      get { return projectPermissionDao ?? (projectPermissionDao = new ProjectPermissionDao(dataContext)); }
+    }
+
     private RequiredPluginDao requiredPluginDao;
     public RequiredPluginDao RequiredPluginDao {
@@ -83,9 +98,4 @@
     }
 
-    private ResourcePermissionDao resourcePermissionDao;
-    public ResourcePermissionDao ResourcePermissionDao {
-      get { return resourcePermissionDao ?? (resourcePermissionDao = new ResourcePermissionDao(dataContext)); }
-    }
-
     private SlaveDao slaveDao;
     public SlaveDao SlaveDao {
@@ -149,4 +159,14 @@
     public FactTaskDao FactTaskDao {
       get { return factTaskDao ?? (factTaskDao = new FactTaskDao(dataContext)); }
+    }
+
+    private DimProjectDao dimProjectDao;
+    public DimProjectDao DimProjectDao {
+      get { return dimProjectDao ?? (dimProjectDao = new DimProjectDao(dataContext)); }
+    }
+
+    private FactProjectInfoDao factProjectInfoDao;
+    public FactProjectInfoDao FactProjectInfoDao {
+      get { return factProjectInfoDao ?? (factProjectInfoDao = new FactProjectInfoDao(dataContext)); }
     }
     #endregion
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Initialize Hive Database.sql
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Initialize Hive Database.sql	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Initialize Hive Database.sql	(revision 16240)
@@ -17,5 +17,5 @@
  * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
  */
-USE [HeuristicLab.Hive-3.3]
+USE [HeuristicLab.Hive-3.4]
 
 EXEC sp_configure filestream_access_level, 2
@@ -25,8 +25,14 @@
 
 SET ARITHABORT ON
-CREATE TABLE [dbo].[AssignedResources](
+
+CREATE TABLE [dbo].[AssignedProjectResource](
   [ResourceId] UniqueIdentifier NOT NULL,
-  [TaskId] UniqueIdentifier NOT NULL,
-  CONSTRAINT [PK_dbo.ResourceIdTaskId] PRIMARY KEY ([ResourceId], [TaskId])
+  [ProjectId] UniqueIdentifier NOT NULL,
+  CONSTRAINT [PK_dbo.ResourceIdProjectId] PRIMARY KEY ([ResourceId], [ProjectId])
+  )
+CREATE TABLE [dbo].[AssignedJobResource](
+  [ResourceId] UniqueIdentifier NOT NULL,
+  [JobId] UniqueIdentifier NOT NULL,
+  CONSTRAINT [PK_dbo.ResourceIdJobId] PRIMARY KEY ([ResourceId], [JobId])
   )
 CREATE TABLE [dbo].[Plugin](
@@ -67,10 +73,4 @@
   CONSTRAINT [PK_dbo.Resource] PRIMARY KEY ([ResourceId])
   )
-CREATE TABLE [dbo].[ResourcePermission](
-  [ResourceId] UniqueIdentifier NOT NULL,
-  [GrantedUserId] UniqueIdentifier NOT NULL,
-  [GrantedByUserId] UniqueIdentifier NOT NULL,
-  CONSTRAINT [PK_dbo.ResourcePermission] PRIMARY KEY ([ResourceId], [GrantedUserId])
-  )
 CREATE TABLE [dbo].[Task](
   [TaskId] UniqueIdentifier NOT NULL,
@@ -103,7 +103,8 @@
   [Name] VarChar(MAX) NOT NULL,
   [Description] VarChar(MAX),
-  [ResourceIds] VarChar(MAX),
   [OwnerUserId] UniqueIdentifier NOT NULL,
   [DateCreated] DateTime NOT NULL,
+  [ProjectId] UniqueIdentifier NOT NULL,
+  [JobState] VarChar(30) NOT NULL,
   CONSTRAINT [PK_dbo.Job] PRIMARY KEY ([JobId])
   )
@@ -148,32 +149,58 @@
   CONSTRAINT [PK_UserPriority] PRIMARY KEY ([UserId])
   )
-ALTER TABLE [dbo].[AssignedResources]
-  ADD CONSTRAINT [Resource_AssignedResource] FOREIGN KEY ([ResourceId]) REFERENCES [dbo].[Resource]([ResourceId])
-ALTER TABLE [dbo].[AssignedResources]
-  ADD CONSTRAINT [Task_AssignedResource] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId])
+CREATE TABLE [dbo].[Project](
+  [ProjectId] UniqueIdentifier NOT NULL,
+  [ParentProjectId] UniqueIdentifier,
+  [DateCreated] DateTime NOT NULL,
+  [Name] VarChar(MAX) NOT NULL,
+  [Description] VarChar(MAX),
+  [OwnerUserId] UniqueIdentifier NOT NULL,
+  [StartDate] DateTime NOT NULL,
+  [EndDate] DateTime,
+  CONSTRAINT [PK_dbo.Project] PRIMARY KEY ([ProjectId])
+  )
+CREATE TABLE [dbo].[ProjectPermission](
+  [ProjectId] UniqueIdentifier NOT NULL,
+  [GrantedUserId] UniqueIdentifier NOT NULL,
+  [GrantedByUserId] UniqueIdentifier NOT NULL,
+  CONSTRAINT [PK_dbo.ProjectPermission] PRIMARY KEY ([ProjectId], [GrantedUserId])
+  )
+
+ALTER TABLE [dbo].[AssignedProjectResource]
+  ADD CONSTRAINT [Resource_AssignedProjectResource] FOREIGN KEY ([ResourceId]) REFERENCES [dbo].[Resource]([ResourceId]) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE [dbo].[AssignedProjectResource]
+  ADD CONSTRAINT [Project_AssignedProjectResource] FOREIGN KEY ([ProjectId]) REFERENCES [dbo].[Project]([ProjectId]) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE [dbo].[AssignedJobResource]
+  ADD CONSTRAINT [Resource_AssignedJobResource] FOREIGN KEY ([ResourceId]) REFERENCES [dbo].[Resource]([ResourceId]) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE [dbo].[AssignedJobResource]
+  ADD CONSTRAINT [Job_AssignedJobResource] FOREIGN KEY ([JobId]) REFERENCES [dbo].[Job]([JobId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[RequiredPlugins]
-  ADD CONSTRAINT [Plugin_RequiredPlugin] FOREIGN KEY ([PluginId]) REFERENCES [dbo].[Plugin]([PluginId])
+  ADD CONSTRAINT [Plugin_RequiredPlugin] FOREIGN KEY ([PluginId]) REFERENCES [dbo].[Plugin]([PluginId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[RequiredPlugins]
-  ADD CONSTRAINT [Task_RequiredPlugin] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId])
+  ADD CONSTRAINT [Task_RequiredPlugin] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[Resource]
-  ADD CONSTRAINT [Resource_Resource] FOREIGN KEY ([ParentResourceId]) REFERENCES [dbo].[Resource]([ResourceId])
-ALTER TABLE [dbo].[ResourcePermission]
-  ADD CONSTRAINT [Resource_ResourcePermission] FOREIGN KEY ([ResourceId]) REFERENCES [dbo].[Resource]([ResourceId])
+  ADD CONSTRAINT [Resource_Resource] FOREIGN KEY ([ParentResourceId]) REFERENCES [dbo].[Resource]([ResourceId]);
 ALTER TABLE [dbo].[Task]
-  ADD CONSTRAINT [Task_Task] FOREIGN KEY ([ParentTaskId]) REFERENCES [dbo].[Task]([TaskId])
+  ADD CONSTRAINT [Task_Task] FOREIGN KEY ([ParentTaskId]) REFERENCES [dbo].[Task]([TaskId]);
 ALTER TABLE [dbo].[Task]
-  ADD CONSTRAINT [Job_Job] FOREIGN KEY ([JobId]) REFERENCES [dbo].[Job]([JobId])
+  ADD CONSTRAINT [Job_Task] FOREIGN KEY ([JobId]) REFERENCES [dbo].[Job]([JobId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[Downtime]
-  ADD CONSTRAINT [Resource_Downtime] FOREIGN KEY ([ResourceId]) REFERENCES [dbo].[Resource]([ResourceId])
+  ADD CONSTRAINT [Resource_Downtime] FOREIGN KEY ([ResourceId]) REFERENCES [dbo].[Resource]([ResourceId]) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE [dbo].[Job]
+	ADD CONSTRAINT [Project_Job] FOREIGN KEY ([ProjectId]) REFERENCES [dbo].[Project]([ProjectId]);
 ALTER TABLE [dbo].[TaskData]
-  ADD CONSTRAINT [Task_TaskData] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId])
+  ADD CONSTRAINT [Task_TaskData] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[PluginData]
-  ADD CONSTRAINT [Plugin_PluginData] FOREIGN KEY ([PluginId]) REFERENCES [dbo].[Plugin]([PluginId])
+  ADD CONSTRAINT [Plugin_PluginData] FOREIGN KEY ([PluginId]) REFERENCES [dbo].[Plugin]([PluginId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[StateLog]
-  ADD CONSTRAINT [Task_StateLog] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId])
+  ADD CONSTRAINT [Task_StateLog] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task]([TaskId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[StateLog]
-  ADD CONSTRAINT [Resource_StateLog] FOREIGN KEY ([SlaveId]) REFERENCES [dbo].[Resource]([ResourceId])
+  ADD CONSTRAINT [Resource_StateLog] FOREIGN KEY ([SlaveId]) REFERENCES [dbo].[Resource]([ResourceId]) ON UPDATE CASCADE ON DELETE CASCADE;
 ALTER TABLE [dbo].[JobPermission]
-  ADD CONSTRAINT [Job_JobPermission] FOREIGN KEY ([JobId]) REFERENCES [dbo].[Job]([JobId])
+  ADD CONSTRAINT [Job_JobPermission] FOREIGN KEY ([JobId]) REFERENCES [dbo].[Job]([JobId]) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE [dbo].[Project]
+  ADD CONSTRAINT [Project_Project] FOREIGN KEY ([ParentProjectId]) REFERENCES [dbo].[Project]([ProjectId]);
+ALTER TABLE [dbo].[ProjectPermission]
+  ADD CONSTRAINT [Project_ProjectPermission] FOREIGN KEY ([ProjectId]) REFERENCES [dbo].[Project]([ProjectId]) ON UPDATE CASCADE ON DELETE CASCADE;
 
 GO
@@ -192,11 +219,10 @@
 CREATE TABLE [statistics].[DimClient] (
     [Id]               UNIQUEIDENTIFIER CONSTRAINT [DF_DimClient_Id] DEFAULT (newsequentialid()) NOT NULL,
-    [Name]             VARCHAR (MAX)    NOT NULL,
     [ResourceId]       UNIQUEIDENTIFIER NOT NULL,
-    [ExpirationTime]   DATETIME         NULL,
-    [ResourceGroupId]  UNIQUEIDENTIFIER NULL,
-    [ResourceGroup2Id] UNIQUEIDENTIFIER NULL,
-    [GroupName]        VARCHAR (MAX)    NULL,
-    [GroupName2]       VARCHAR (MAX)    NULL,
+	[ParentResourceId] UNIQUEIDENTIFIER NULL,
+	[Name]             VARCHAR (MAX)    NOT NULL,
+	[ResourceType]     VARCHAR (MAX)    NULL,
+	[DateCreated]      DATETIME         NOT NULL,
+    [DateExpired]      DATETIME         NULL
     CONSTRAINT [PK_DimClient] PRIMARY KEY CLUSTERED ([Id] ASC)
 );
@@ -204,4 +230,5 @@
     [JobId]          UNIQUEIDENTIFIER NOT NULL,
     [UserId]         UNIQUEIDENTIFIER NOT NULL,
+	[ProjectId]      UNIQUEIDENTIFIER NOT NULL,
     [JobName]        VARCHAR (MAX)    NOT NULL,
     [UserName]       VARCHAR (MAX)    NOT NULL,
@@ -212,8 +239,35 @@
     CONSTRAINT [PK_DimJob] PRIMARY KEY CLUSTERED ([JobId] ASC)
 );
+CREATE TABLE [statistics].[DimProject] (
+    [Id]               UNIQUEIDENTIFIER CONSTRAINT [DF_DimProject_Id] DEFAULT (newsequentialid()) NOT NULL,
+    [ProjectId]        UNIQUEIDENTIFIER NOT NULL,
+	[ParentProjectId]  UNIQUEIDENTIFIER NULL,
+	[Name]             VARCHAR (MAX)    NOT NULL,
+	[Description]      VARCHAR (MAX)    NULL,
+	[OwnerUserId]      UNIQUEIDENTIFIER NOT NULL,
+	[StartDate]        DATETIME         NOT NULL,
+    [EndDate]          DATETIME         NULL,
+	[DateCreated]      DATETIME         NOT NULL,
+    [DateExpired]      DATETIME         NULL
+	CONSTRAINT [PK_DimProject] PRIMARY KEY CLUSTERED ([Id] ASC)
+);
 CREATE TABLE [statistics].[DimUser] (
     [UserId] UNIQUEIDENTIFIER NOT NULL,
     [Name]   VARCHAR (MAX)    NOT NULL,
     CONSTRAINT [PK_DimUser] PRIMARY KEY CLUSTERED ([UserId] ASC)
+);
+
+
+
+CREATE TABLE [statistics].[FactProjectInfo] (
+	[ProjectId]		UNIQUEIDENTIFIER NOT NULL,
+	[Time]			DATETIME		 NOT NULL,
+	[NumTotalCores]	INT				 NOT NULL,
+	[NumUsedCores]  INT				 NOT NULL,
+	[TotalMemory]	INT				 NOT NULL,
+	[UsedMemory]	INT				 NOT NULL
+	CONSTRAINT [PK_FactProjectInfo] PRIMARY KEY CLUSTERED ([ProjectId] ASC, [Time] ASC),
+	CONSTRAINT [FK_FactProjectInfo_DimTime] FOREIGN KEY ([Time]) REFERENCES [statistics].[DimTime] ([Time]),
+	CONSTRAINT [FK_FactProjectInfo_DimProject] FOREIGN KEY ([ProjectId]) REFERENCES [statistics].[DimProject] ([Id])
 );
 CREATE TABLE [statistics].[FactClientInfo] (
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Prepare Hive Database.sql
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Prepare Hive Database.sql	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Prepare Hive Database.sql	(revision 16240)
@@ -19,14 +19,26 @@
 
 /* this script is supposed to be executed after the plain DB is generated by the linq-to-sql schema */
-USE [HeuristicLab.Hive-3.3]
-
-ALTER TABLE [dbo].[AssignedResources]  DROP  CONSTRAINT [Task_AssignedResource]
-ALTER TABLE [dbo].[AssignedResources]  WITH CHECK ADD  CONSTRAINT [Task_AssignedResource] FOREIGN KEY([TaskId])
-REFERENCES [dbo].[Task] ([TaskId])
-ON UPDATE CASCADE
-ON DELETE CASCADE
-GO
-ALTER TABLE [dbo].[AssignedResources]  DROP  CONSTRAINT [Resource_AssignedResource]
-ALTER TABLE [dbo].[AssignedResources]  WITH CHECK ADD  CONSTRAINT [Resource_AssignedResource] FOREIGN KEY([ResourceId])
+USE [HeuristicLab.Hive-3.4]
+
+ALTER TABLE [dbo].[AssignedProjectResource]  DROP  CONSTRAINT [Project_AssignedProjectResource]
+ALTER TABLE [dbo].[AssignedProjectResource]  WITH CHECK ADD  CONSTRAINT [Project_AssignedProjectResource] FOREIGN KEY([ProjectId])
+REFERENCES [dbo].[Project] ([ProjectId])
+ON UPDATE CASCADE
+ON DELETE CASCADE
+GO
+ALTER TABLE [dbo].[AssignedProjectResource]  DROP  CONSTRAINT [Resource_AssignedProjectResource]
+ALTER TABLE [dbo].[AssignedProjectResource]  WITH CHECK ADD  CONSTRAINT [Resource_AssignedProjectResource] FOREIGN KEY([ResourceId])
+REFERENCES [dbo].[Resource] ([ResourceId])
+ON UPDATE CASCADE
+ON DELETE CASCADE
+GO
+ALTER TABLE [dbo].[AssignedJobResource]  DROP  CONSTRAINT [Job_AssignedJobResource]
+ALTER TABLE [dbo].[AssignedJobResource]  WITH CHECK ADD  CONSTRAINT [Job_AssignedJobResource] FOREIGN KEY([JobId])
+REFERENCES [dbo].[Job] ([JobId])
+ON UPDATE CASCADE
+ON DELETE CASCADE
+GO
+ALTER TABLE [dbo].[AssignedJobResource]  DROP  CONSTRAINT [Resource_AssignedJobResource]
+ALTER TABLE [dbo].[AssignedJobResource]  WITH CHECK ADD  CONSTRAINT [Resource_AssignedJobResource] FOREIGN KEY([ResourceId])
 REFERENCES [dbo].[Resource] ([ResourceId])
 ON UPDATE CASCADE
@@ -88,4 +100,9 @@
 ALTER TABLE dbo.Job ALTER COLUMN JobId ADD ROWGUIDCOL;
 ALTER TABLE dbo.Job WITH NOCHECK ADD CONSTRAINT [DF_Job_JobId] DEFAULT (NEWSEQUENTIALID()) FOR JobId;
+ALTER TABLE [dbo].[Job]  DROP  CONSTRAINT [Project_Job]
+ALTER TABLE [dbo].[Job]  WITH CHECK ADD  CONSTRAINT [Project_Job] FOREIGN KEY([ProjectId])
+REFERENCES [dbo].[Project] ([ProjectId])
+ON UPDATE CASCADE
+GO
 
 ALTER TABLE dbo.StateLog ALTER COLUMN StateLogId ADD ROWGUIDCOL;
@@ -99,7 +116,10 @@
 GO
 
-ALTER TABLE [dbo].[ResourcePermission]  DROP  CONSTRAINT [Resource_ResourcePermission]
-ALTER TABLE [dbo].[ResourcePermission]  WITH CHECK ADD CONSTRAINT [Resource_ResourcePermission] FOREIGN KEY([ResourceId])
-REFERENCES [dbo].[Resource] ([ResourceId])
+ALTER TABLE dbo.Project ALTER COLUMN ProjectId ADD ROWGUIDCOL;
+ALTER TABLE dbo.Project WITH NOCHECK ADD CONSTRAINT [DF_Project_ProjectId] DEFAULT (NEWSEQUENTIALID()) FOR ProjectId;
+
+ALTER TABLE [dbo].[ProjectPermission]  DROP  CONSTRAINT [Project_ProjectPermission]
+ALTER TABLE [dbo].[ProjectPermission]  WITH CHECK ADD CONSTRAINT [Project_ProjectPermission] FOREIGN KEY([ProjectId])
+REFERENCES [dbo].[Project] ([ProjectId])
 ON UPDATE CASCADE
 ON DELETE CASCADE
@@ -123,4 +143,6 @@
 
 
+
+-- OBSOLETE - DO NOT PERFORM (start)
 /****** Object:  Trigger [dbo].[tr_JobDeleteCascade]    Script Date: 04/19/2011 16:31:53 ******/
 SET ANSI_NULLS ON
@@ -175,4 +197,4 @@
 END
 GO
-
-
+-- OBSOLETE (end)
+
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Settings.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Settings.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Settings.Designer.cs	(revision 16240)
@@ -13,5 +13,5 @@
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
     public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
         
@@ -21,15 +21,4 @@
             get {
                 return defaultInstance;
-            }
-        }
-        
-        [global::System.Configuration.ApplicationScopedSettingAttribute()]
-        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-        [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
-        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=.\\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hi" +
-            "ve-3.3")]
-        public string HeuristicLab_Hive_LinqConnectionString {
-            get {
-                return ((string)(this["HeuristicLab_Hive_LinqConnectionString"]));
             }
         }
@@ -52,4 +41,15 @@
             }
         }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-" +
+            "3.4")]
+        public string HeuristicLab_Hive_LinqConnectionString {
+            get {
+                return ((string)(this["HeuristicLab_Hive_LinqConnectionString"]));
+            }
+        }
     }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Settings.settings
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Settings.settings	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/Settings.settings	(revision 16240)
@@ -3,12 +3,4 @@
   <Profiles />
   <Settings>
-    <Setting Name="HeuristicLab_Hive_LinqConnectionString" Type="(Connection string)" Scope="Application">
-      <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
-&lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
-  &lt;ConnectionString&gt;Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.3&lt;/ConnectionString&gt;
-  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
-&lt;/SerializableConnectionString&gt;</DesignTimeValue>
-      <Value Profile="(Default)">Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.3</Value>
-    </Setting>
     <Setting Name="LongRunningDatabaseCommandTimeout" Type="System.TimeSpan" Scope="Application">
       <Value Profile="(Default)">00:05:00</Value>
@@ -17,4 +9,11 @@
       <Value Profile="(Default)">HL.Hive</Value>
     </Setting>
+    <Setting Name="HeuristicLab_Hive_LinqConnectionString" Type="(Connection string)" Scope="Application">
+      <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
+&lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
+  &lt;ConnectionString&gt;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.4&lt;/ConnectionString&gt;
+&lt;/SerializableConnectionString&gt;</DesignTimeValue>
+      <Value Profile="(Default)">Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.4</Value>
+    </Setting>
   </Settings>
 </SettingsFile>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.DataAccess/3.3/app.config	(revision 16240)
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
+  <configSections>
+  </configSections>
   <connectionStrings>
-    <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.4" />
+    <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString"
+      connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.4" />
   </connectionStrings>
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.JanitorService/3.3/ProjectInstaller.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.JanitorService/3.3/ProjectInstaller.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.JanitorService/3.3/ProjectInstaller.Designer.cs	(revision 16240)
@@ -50,5 +50,5 @@
       // serviceProcessInstaller1
       // 
-      this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
+      this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.NetworkService;
       this.serviceProcessInstaller1.Installers.AddRange(new System.Configuration.Install.Installer[] {
             this.serviceInstaller1});
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.Web/Hive-3.3/Web.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.Web/Hive-3.3/Web.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.Web/Hive-3.3/Web.config	(revision 16240)
@@ -23,6 +23,6 @@
   
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
-    <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.3" />
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.4" />
   </connectionStrings>
 
@@ -62,10 +62,10 @@
         <endpoint binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHiveService"
           name="WSHttpBinding_IHiveService" contract="HeuristicLab.Services.Hive.ServiceContracts.IHiveService" />
-        <endpoint address="net.tcp://localhost:8001/Hive-3.3/HiveService.svc"
+        <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.4/HiveService.svc"
           binding="netTcpBinding" bindingConfiguration="NetBinding_IHiveService"
           name="NetBinding_IHiveService" contract="HeuristicLab.Services.Hive.ServiceContracts.IHiveService" />
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/Hive-3.3/HiveService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/Hive-3.4/HiveService.svc" />
           </baseAddresses>
         </host>
@@ -101,5 +101,5 @@
           <serviceDebug includeExceptionDetailInFaults="true" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.Web/web_services.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.Web/web_services.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive.Web/web_services.config	(revision 16240)
@@ -25,5 +25,5 @@
     <add name="HeuristicLab.Authentication" connectionString="data source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication"/>
 	 <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString"
-      connectionString="Data Source=localhost;Initial Catalog=HeuristicLab.Hive-3.3;Integrated Security=True;"
+      connectionString="Data Source=localhost;Initial Catalog=HeuristicLab.Hive-3.4;Integrated Security=True;"
       providerName="System.Data.SqlClient" />
   </connectionStrings>
@@ -118,5 +118,5 @@
         <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" name="MexTcpEndpoint" contract="IMetadataExchange"/>
         <endpoint address="" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHiveService" name="WSHttpBinding_IHiveService" contract="HeuristicLab.Services.Hive.ServiceContracts.IHiveService"/>
-        <endpoint address="net.tcp://services.heuristiclab.com/Hive-3.3/HiveService.svc" binding="netTcpBinding"  bindingConfiguration="NetTcpBinding_IHiveService" name="NetBinding_IHiveService" contract="HeuristicLab.Services.Hive.ServiceContracts.IHiveService"/>
+        <endpoint address="net.tcp://10.20.71.54/Hive-3.4/HiveService.svc" binding="netTcpBinding"  bindingConfiguration="NetTcpBinding_IHiveService" name="NetBinding_IHiveService" contract="HeuristicLab.Services.Hive.ServiceContracts.IHiveService"/>
       </service>
     </services>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Converter.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Converter.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Converter.cs	(revision 16240)
@@ -121,5 +121,6 @@
         OwnerUserId = source.OwnerUserId,
         DateCreated = source.DateCreated,
-        ResourceNames = source.ResourceIds
+        ProjectId = source.ProjectId,
+        State = source.State.ToDto()
       };
     }
@@ -139,5 +140,27 @@
       target.OwnerUserId = source.OwnerUserId;
       target.DateCreated = source.DateCreated;
-      target.ResourceIds = source.ResourceNames;
+      target.ProjectId = source.ProjectId;
+      target.State = source.State.ToEntity();
+    }
+    #endregion
+
+    #region AssignedJobResource
+    public static DT.AssignedJobResource ToDto(this DA.AssignedJobResource source) {
+      if (source == null) return null;
+      return new DT.AssignedJobResource {
+        JobId = source.JobId,
+        ResourceId = source.ResourceId
+      };
+    }
+    public static DA.AssignedJobResource ToEntity(this DT.AssignedJobResource source) {
+      if (source == null) return null;
+      var result = new DA.AssignedJobResource();
+      source.CopyToEntity(result);
+      return result;
+    }
+    public static void CopyToEntity(this DT.AssignedJobResource source, DA.AssignedJobResource target) {
+      if ((source == null) || (target == null)) return;
+      target.JobId = source.JobId;
+      target.ResourceId = source.ResourceId;
     }
     #endregion
@@ -221,5 +244,5 @@
     #endregion
 
-    #region State
+    #region TaskState
     public static DT.TaskState ToDto(this DA.TaskState source) {
       switch (source) {
@@ -247,4 +270,24 @@
         case DT.TaskState.Waiting: return DA.TaskState.Waiting;
         default: return DA.TaskState.Failed;
+      }
+    }
+    #endregion
+
+    #region JobState
+    public static DT.JobState ToDto(this DA.JobState source) {
+      switch (source) {
+        case DA.JobState.Online: return DT.JobState.Online;
+        case DA.JobState.StatisticsPending: return DT.JobState.StatisticsPending;
+        case DA.JobState.DeletionPending: return DT.JobState.DeletionPending;
+        default: return DT.JobState.Online;
+      }
+    }
+
+    public static DA.JobState ToEntity(this DT.JobState source) {
+      switch (source) {
+        case DT.JobState.Online: return DA.JobState.Online;
+        case DT.JobState.StatisticsPending: return DA.JobState.StatisticsPending;
+        case DT.JobState.DeletionPending: return DA.JobState.DeletionPending;
+        default: return DA.JobState.Online;
       }
     }
@@ -333,24 +376,78 @@
     #endregion
 
-    #region ResourcePermission
-    public static DT.ResourcePermission ToDto(this DA.ResourcePermission source) {
-      if (source == null) return null;
-      return new DT.ResourcePermission {
-        ResourceId = source.ResourceId,
+    #region Project
+    public static DT.Project ToDto(this DA.Project source) {
+      if (source == null) return null;
+      return new DT.Project {
+        Id = source.ProjectId,
+        ParentProjectId = source.ParentProjectId,
+        DateCreated = source.DateCreated,
+        Name = source.Name,
+        Description = source.Description,
+        OwnerUserId = source.OwnerUserId,
+        StartDate = source.StartDate,
+        EndDate = source.EndDate
+      };
+    }
+    public static DA.Project ToEntity(this DT.Project source) {
+      if (source == null) return null;
+      var result = new DA.Project();
+      source.CopyToEntity(result);
+      return result;
+    }
+    public static void CopyToEntity(this DT.Project source, DA.Project target) {
+      if ((source == null) || (target == null)) return;
+      target.ProjectId = source.Id;
+      target.ParentProjectId = source.ParentProjectId;
+      target.DateCreated = source.DateCreated;
+      target.Name = source.Name;
+      target.Description = source.Description;
+      target.OwnerUserId = source.OwnerUserId;
+      target.StartDate = source.StartDate;
+      target.EndDate = source.EndDate;
+    }
+    #endregion
+
+    #region ProjectPermission
+    public static DT.ProjectPermission ToDto(this DA.ProjectPermission source) {
+      if (source == null) return null;
+      return new DT.ProjectPermission {
+        ProjectId = source.ProjectId,
         GrantedUserId = source.GrantedUserId,
         GrantedByUserId = source.GrantedByUserId
       };
     }
-    public static DA.ResourcePermission ToEntity(this DT.ResourcePermission source) {
-      if (source == null) return null;
-      var result = new DA.ResourcePermission();
-      source.CopyToEntity(result);
-      return result;
-    }
-    public static void CopyToEntity(this DT.ResourcePermission source, DA.ResourcePermission target) {
-      if ((source == null) || (target == null)) return;
-      target.ResourceId = source.ResourceId;
+    public static DA.ProjectPermission ToEntity(this DT.ProjectPermission source) {
+      if (source == null) return null;
+      var result = new DA.ProjectPermission();
+      source.CopyToEntity(result);
+      return result;
+    }
+    public static void CopyToEntity(this DT.ProjectPermission source, DA.ProjectPermission target) {
+      if ((source == null) || (target == null)) return;
+      target.ProjectId = source.ProjectId;
       target.GrantedUserId = source.GrantedUserId;
       target.GrantedByUserId = source.GrantedByUserId;
+    }
+    #endregion
+
+    #region AssignedProjectResource
+    public static DT.AssignedProjectResource ToDto(this DA.AssignedProjectResource source) {
+      if (source == null) return null;
+      return new DT.AssignedProjectResource {
+        ProjectId = source.ProjectId,
+        ResourceId = source.ResourceId
+      };
+    }
+    public static DA.AssignedProjectResource ToEntity(this DT.AssignedProjectResource source) {
+      if (source == null) return null;
+      var result = new DA.AssignedProjectResource();
+      source.CopyToEntity(result);
+      return result;
+    }
+    public static void CopyToEntity(this DT.AssignedProjectResource source, DA.AssignedProjectResource target) {
+      if ((source == null) || (target == null)) return;
+      target.ProjectId = source.ProjectId;
+      target.ResourceId = source.ResourceId;
     }
     #endregion
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/AssignedJobResource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/AssignedJobResource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/AssignedJobResource.cs	(revision 16240)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Services.Hive.DataTransfer {
+  [DataContract]
+  [Serializable]
+  public class AssignedJobResource : HiveItem {
+    [DataMember]
+    public Guid JobId { get; set; }
+    [DataMember]
+    public Guid ResourceId { get; set; }
+
+    public AssignedJobResource() { }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/AssignedProjectResource.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/AssignedProjectResource.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/AssignedProjectResource.cs	(revision 16240)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Services.Hive.DataTransfer {
+  [DataContract]
+  [Serializable]
+  public class AssignedProjectResource : HiveItem {
+    [DataMember]
+    public Guid ProjectId { get; set; }
+    [DataMember]
+    public Guid ResourceId { get; set; }
+
+    public AssignedProjectResource() { }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/Job.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/Job.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/Job.cs	(revision 16240)
@@ -32,9 +32,11 @@
     public DateTime DateCreated { get; set; }
     [DataMember]
-    public string ResourceNames { get; set; }
+    public Guid ProjectId { get; set; }
     [DataMember]
     public Permission Permission { get; set; } // the permission for the currently logged in user
     [DataMember]
     public string OwnerUsername { get; set; }
+    [DataMember]
+    public JobState State { get; set; }
 
     /* ==== some computed statistics ==== */
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/JobState.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/JobState.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/JobState.cs	(revision 16240)
@@ -0,0 +1,45 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HeuristicLab.Services.Hive.DataTransfer {
+  [Serializable]
+  public enum JobState {
+    /// <summary>
+    /// A job is online as long as he is no other state.
+    /// </summary>
+    Online,
+
+    /// <summary>
+    /// A job is in StatisticsPending if its deletion has been requested,
+    /// but the final generation of statistics hasn't been performed yet.
+    /// </summary>
+    StatisticsPending,
+
+    /// <summary>
+    /// A job is in DeletionPending if its deletion has been requested,
+    /// the final generation of statistics has already been performed,
+    /// but the eventual deletion of the job is still pending. 
+    /// </summary>
+    DeletionPending
+  };
+
+  public static class JobStateExtensions {
+    /// <summary>
+    /// This job is online
+    /// </summary>
+    public static bool IsOnline(this JobState jobState) {
+      return jobState == JobState.Online;
+    }
+
+    /// <summary>
+    /// This job is still existent, but already flagged for deletion.
+    /// Usually the flag is set by a user, not a resource or the janitor or something else.
+    /// </summary>
+    public static bool IsFlaggedForDeletion(this JobState jobState) {
+      return jobState == JobState.StatisticsPending || jobState == JobState.DeletionPending;
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/Project.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/Project.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/Project.cs	(revision 16240)
@@ -0,0 +1,42 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Services.Hive.DataTransfer {
+  [DataContract]
+  [Serializable]
+  public class Project : NamedHiveItem {
+    [DataMember]
+    public Guid? ParentProjectId { get; set; }
+    [DataMember]
+    public DateTime DateCreated { get; set; }
+    [DataMember]
+    public Guid OwnerUserId { get; set; }
+    [DataMember]
+    public DateTime StartDate { get; set; }
+    [DataMember]
+    public DateTime? EndDate { get; set; }
+
+    public Project() { }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/ProjectPermission.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/ProjectPermission.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/ProjectPermission.cs	(revision 16240)
@@ -0,0 +1,39 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Services.Hive.DataTransfer {
+  [DataContract]
+  [Serializable]
+  public class ProjectPermission : HiveItem {
+    // info: this class is derived from HiveItem to simplify handling on the client side, altough it does not have a Id.
+    [DataMember]
+    public Guid ProjectId { get; set; }
+    [DataMember]
+    public Guid GrantedUserId { get; set; }
+    [DataMember]
+    public Guid GrantedByUserId { get; set; }
+
+    public ProjectPermission() { }
+  }
+}
Index: anches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/ResourcePermission.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/DataTransfer/ResourcePermission.cs	(revision 16239)
+++ 	(revision )
@@ -1,39 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Runtime.Serialization;
-
-namespace HeuristicLab.Services.Hive.DataTransfer {
-  [DataContract]
-  [Serializable]
-  public class ResourcePermission : HiveItem {
-    // info: this class is derived from HiveItem to simplify handling on the client side, altough it does not have a Id.
-    [DataMember]
-    public Guid ResourceId { get; set; }
-    [DataMember]
-    public Guid GrantedUserId { get; set; }
-    [DataMember]
-    public Guid GrantedByUserId { get; set; }
-
-    public ResourcePermission() { }
-  }
-}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HeuristicLab.Services.Hive-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HeuristicLab.Services.Hive-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HeuristicLab.Services.Hive-3.3.csproj	(revision 16240)
@@ -119,7 +119,11 @@
   <ItemGroup>
     <Compile Include="Converter.cs" />
+    <Compile Include="DataTransfer\AssignedJobResource.cs" />
+    <Compile Include="DataTransfer\AssignedProjectResource.cs" />
     <Compile Include="DataTransfer\Command.cs" />
+    <Compile Include="DataTransfer\JobState.cs" />
+    <Compile Include="DataTransfer\Project.cs" />
+    <Compile Include="DataTransfer\ProjectPermission.cs" />
     <Compile Include="DataTransfer\UserPriority.cs" />
-    <Compile Include="DataTransfer\ResourcePermission.cs" />
     <Compile Include="DataTransfer\Downtime.cs" />
     <Compile Include="DataTransfer\Heartbeat.cs" />
@@ -149,5 +153,7 @@
     <Compile Include="Scheduler\JobInfoForScheduler.cs" />
     <Compile Include="Scheduler\RoundRobinTaskScheduler.cs" />
-    <None Include="app.config" />
+    <None Include="app.config">
+      <SubType>Designer</SubType>
+    </None>
     <None Include="Plugin.cs.frame" />
     <None Include="Properties\AssemblyInfo.cs.frame" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HiveService.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HiveService.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HiveService.cs	(revision 16240)
@@ -41,4 +41,9 @@
   [HiveOperationContextBehavior]
   public class HiveService : IHiveService {
+    private const string NOT_AUTHORIZED_PROJECTRESOURCE = "Selected project is not authorized to access the requested resource";
+    private const string NOT_AUTHORIZED_USERPROJECT = "Current user is not authorized to access the requested project";
+    private const string NOT_AUTHORIZED_PROJECTOWNER = "The set user is not authorized to own the project";
+    private const string NO_JOB_UPDATE_POSSIBLE = "This job has already been flagged for deletion, thus, it can not be updated anymore.";
+
     private static readonly DA.TaskState[] CompletedStates = { DA.TaskState.Finished, DA.TaskState.Aborted, DA.TaskState.Failed };
 
@@ -66,6 +71,8 @@
 
     #region Task Methods
-    public Guid AddTask(DT.Task task, DT.TaskData taskData, IEnumerable<Guid> resourceIds) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+
+    public Guid AddTask(DT.Task task, DT.TaskData taskData) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      AuthorizationManager.AuthorizeForJob(task.JobId, DT.Permission.Full);
       var pm = PersistenceManager;
       using (new PerformanceLogger("AddTask")) {
@@ -75,8 +82,4 @@
         newTask.JobData = taskData.ToEntity();
         newTask.JobData.LastUpdate = DateTime.Now;
-        newTask.AssignedResources.AddRange(resourceIds.Select(
-          x => new DA.AssignedResource {
-            ResourceId = x
-          }));
         newTask.State = DA.TaskState.Waiting;
         return pm.UseTransaction(() => {
@@ -99,16 +102,6 @@
     public Guid AddChildTask(Guid parentTaskId, DT.Task task, DT.TaskData taskData) {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      IEnumerable<Guid> resourceIds;
-      var pm = PersistenceManager;
-      using (new PerformanceLogger("AddChildTask")) {
-        var assignedResourceDao = pm.AssignedResourceDao;
-        resourceIds = pm.UseTransaction(() => {
-          return assignedResourceDao.GetByTaskId(parentTaskId)
-            .Select(x => x.ResourceId)
-            .ToList();
-        });
-      }
       task.ParentTaskId = parentTaskId;
-      return AddTask(task, taskData, resourceIds);
+      return AddTask(task, taskData);
     }
 
@@ -338,52 +331,123 @@
         return pm.UseTransaction(() => {
           var jobs = jobDao.GetAll()
-            .Where(x => x.OwnerUserId == currentUserId
-                     || x.JobPermissions.Count(y => y.Permission != DA.Permission.NotAllowed
-                                                 && y.GrantedUserId == currentUserId) > 0)
+            .Where(x => x.State == DA.JobState.Online
+                          && (x.OwnerUserId == currentUserId
+                            || x.JobPermissions.Count(y => y.Permission != DA.Permission.NotAllowed
+                              && y.GrantedUserId == currentUserId) > 0)
+                          )
             .Select(x => x.ToDto())
             .ToList();
-          var statistics = taskDao.GetAll()
-              .GroupBy(x => x.JobId)
-              .Select(x => new {
-                x.Key,
-                TotalCount = x.Count(),
-                CalculatingCount = x.Count(y => y.State == DA.TaskState.Calculating),
-                FinishedCount = x.Count(y => CompletedStates.Contains(y.State))
-              })
+
+          EvaluateJobs(pm, jobs);
+          return jobs;
+        });
+      }
+    }
+
+    public IEnumerable<DT.Job> GetJobsByProjectId(Guid projectId) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetJobsByProjectId")) {
+        var currentUserId = UserManager.CurrentUserId;
+        var projectDao = pm.ProjectDao;
+        var jobDao = pm.JobDao;
+
+        return pm.UseTransaction(() => {
+          // check if user is granted to administer the requested projectId
+          bool isAdmin = RoleVerifier.IsInRole(HiveRoles.Administrator);
+          List<DA.Project> administrationGrantedProjects;
+          if (isAdmin) {
+            administrationGrantedProjects = projectDao.GetAll().ToList();
+          } else {
+            administrationGrantedProjects = projectDao
+              .GetAdministrationGrantedProjectsForUser(currentUserId)              
               .ToList();
-          foreach (var job in jobs) {
-            var statistic = statistics.FirstOrDefault(x => x.Key == job.Id);
-            if (statistic != null) {
-              job.JobCount = statistic.TotalCount;
-              job.CalculatingCount = statistic.CalculatingCount;
-              job.FinishedCount = statistic.FinishedCount;
-            }
-            job.OwnerUsername = UserManager.GetUserNameById(job.OwnerUserId);
-            if (currentUserId == job.OwnerUserId) {
-              job.Permission = Permission.Full;
-            } else {
-              var jobPermission = jobPermissionDao.GetByJobAndUserId(job.Id, currentUserId);
-              job.Permission = jobPermission == null ? Permission.NotAllowed : jobPermission.Permission.ToDto();
-            }
-          }
-          return jobs;
-        });
-      }
-    }
-
-    public Guid AddJob(DT.Job jobDto) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      var pm = PersistenceManager;
+          }
+
+          if (administrationGrantedProjects.Select(x => x.ProjectId).Contains(projectId)) {
+            var jobs = jobDao.GetByProjectId(projectId)
+            .Select(x => x.ToDto())
+            .ToList();
+
+            EvaluateJobs(pm, jobs);
+            return jobs;
+          } else {
+            return Enumerable.Empty<DT.Job>();
+          }
+        });
+      }
+    }
+
+    public IEnumerable<DT.Job> GetJobsByProjectIds(IEnumerable<Guid> projectIds) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetJobsByProjectIds")) {
+        var currentUserId = UserManager.CurrentUserId;
+        var projectDao = pm.ProjectDao;
+        var jobDao = pm.JobDao;
+        return pm.UseTransaction(() => {
+          // check for which of requested projectIds the user is granted to administer
+          bool isAdmin = RoleVerifier.IsInRole(HiveRoles.Administrator);
+          List<Guid> administrationGrantedProjectIds;
+          if (isAdmin) {
+            administrationGrantedProjectIds = projectDao.GetAll().Select(x => x.ProjectId).ToList();
+          } else {
+            administrationGrantedProjectIds = projectDao
+              .GetAdministrationGrantedProjectsForUser(currentUserId)
+              .Select(x => x.ProjectId)
+              .ToList();
+          }
+          var requestedAndGrantedProjectIds = projectIds.Intersect(administrationGrantedProjectIds);
+
+          if (requestedAndGrantedProjectIds.Any()) {
+            var jobs = jobDao.GetByProjectIds(requestedAndGrantedProjectIds)
+              .Select(x => x.ToDto())
+              .ToList();
+
+            EvaluateJobs(pm, jobs);
+            return jobs;
+          } else {
+            return Enumerable.Empty<DT.Job>();
+          }
+        });
+      }
+    }
+
+    public Guid AddJob(DT.Job jobDto, IEnumerable<Guid> resourceIds) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var dateCreated = DateTime.Now;
+      var pm = PersistenceManager;
+
+      // check project availability (cf. duration)
+      CheckProjectAvailability(pm, jobDto.ProjectId, dateCreated);
+
+      // check user - project
+      AuthorizationManager.AuthorizeUserForProjectUse(UserManager.CurrentUserId, jobDto.ProjectId);
+
+      // check project - resources
+      AuthorizationManager.AuthorizeProjectForResourcesUse(jobDto.ProjectId, resourceIds);
+
       using (new PerformanceLogger("AddJob")) {
         var jobDao = pm.JobDao;
         var userPriorityDao = pm.UserPriorityDao;
-        return pm.UseTransaction(() => {
-          jobDto.OwnerUserId = UserManager.CurrentUserId;
-          jobDto.DateCreated = DateTime.Now;
-          var job = jobDao.Save(jobDto.ToEntity());
-          if (userPriorityDao.GetById(jobDto.OwnerUserId) == null) {
+
+        return pm.UseTransaction(() => {
+          var newJob = jobDto.ToEntity();
+          newJob.OwnerUserId = UserManager.CurrentUserId;
+          newJob.DateCreated = dateCreated;
+
+          // add resource assignments
+          if (resourceIds != null && resourceIds.Any()) {
+            newJob.AssignedJobResources.AddRange(resourceIds.Select(
+              x => new DA.AssignedJobResource {
+                ResourceId = x
+              }));
+          }
+
+          var job = jobDao.Save(newJob);
+          if (userPriorityDao.GetById(newJob.OwnerUserId) == null) {
             userPriorityDao.Save(new DA.UserPriority {
-              UserId = jobDto.OwnerUserId,
-              DateEnqueued = jobDto.DateCreated
+              UserId = newJob.OwnerUserId,
+              DateEnqueued = newJob.DateCreated
             });
           }
@@ -394,8 +458,17 @@
     }
 
-    public void UpdateJob(DT.Job jobDto) {
+    public void UpdateJob(DT.Job jobDto, IEnumerable<Guid> resourceIds) {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
       AuthorizationManager.AuthorizeForJob(jobDto.Id, DT.Permission.Full);
       var pm = PersistenceManager;
+      var dateUpdated = DateTime.Now;
+
+      // check project availability
+      CheckProjectAvailability(pm, jobDto.ProjectId, dateUpdated);
+      // check user - project permission
+      AuthorizationManager.AuthorizeUserForProjectUse(UserManager.CurrentUserId, jobDto.ProjectId);
+      // check project - resources permission
+      AuthorizationManager.AuthorizeProjectForResourcesUse(jobDto.ProjectId, resourceIds);
+
       using (new PerformanceLogger("UpdateJob")) {
         bool exists = true;
@@ -406,25 +479,120 @@
             exists = false;
             job = new DA.Job();
-          }
+          } else if (job.State != DA.JobState.Online) {
+            throw new InvalidOperationException(NO_JOB_UPDATE_POSSIBLE);
+          }
+
           jobDto.CopyToEntity(job);
+
           if (!exists) {
+            // add resource assignments
+            if (resourceIds != null && resourceIds.Any()) {
+              job.AssignedJobResources.AddRange(resourceIds.Select(
+                x => new DA.AssignedJobResource {
+                  ResourceId = x
+                }));
+            }
             jobDao.Save(job);
-          }
-          pm.SubmitChanges();
-        });
-      }
-    }
-
-    public void DeleteJob(Guid jobId) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+          } else if (resourceIds != null) {
+            var addedJobResourceIds = resourceIds.Except(job.AssignedJobResources.Select(x => x.ResourceId));
+            var removedJobResourceIds = job.AssignedJobResources
+              .Select(x => x.ResourceId)
+              .Except(resourceIds)
+              .ToArray();
+
+            // remove resource assignments
+            foreach (var rid in removedJobResourceIds) {
+              var ajr = job.AssignedJobResources.Where(x => x.ResourceId == rid).SingleOrDefault();
+              if (ajr != null) job.AssignedJobResources.Remove(ajr);
+            }
+
+            // add resource assignments
+            job.AssignedJobResources.AddRange(addedJobResourceIds.Select(
+              x => new DA.AssignedJobResource {
+                ResourceId = x
+              }));
+          }
+          pm.SubmitChanges();
+        });
+      }
+    }
+
+    public void UpdateJobState(Guid jobId, DT.JobState jobState) {
+      if (jobState != JobState.StatisticsPending) return; // only process requests for "StatisticsPending"
+
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      // check if user is an admin, or granted to administer a job-parenting project, or job owner
       AuthorizationManager.AuthorizeForJob(jobId, DT.Permission.Full);
-      var pm = PersistenceManager;
-      using (new PerformanceLogger("DeleteJob")) {
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("UpdateJobState")) {
+        var jobDao = pm.JobDao;        
+        pm.UseTransaction(() => {
+          var job = jobDao.GetById(jobId);
+          if (job != null) {            
+
+            // note: allow solely state changes from "Online" to "StatisticsPending" = deletion request by user for HiveStatisticGenerator            
+            var jobStateEntity = jobState.ToEntity();
+            if (job.State == DA.JobState.Online && jobStateEntity == DA.JobState.StatisticsPending) {
+              job.State = jobStateEntity;
+              foreach (var task in job.Tasks
+              .Where(x => x.State == DA.TaskState.Waiting
+                || x.State == DA.TaskState.Paused
+                || x.State == DA.TaskState.Offline)) {
+                task.State = DA.TaskState.Aborted;
+              }
+              pm.SubmitChanges();
+            }
+          }
+        });
+      }
+    }
+
+    public void UpdateJobStates(IEnumerable<Guid> jobIds, DT.JobState jobState) {
+      if (jobState != JobState.StatisticsPending) return; // only process requests for "StatisticsPending"
+
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      // check if user is an admin, or granted to administer a job-parenting project, or job owner
+      foreach (var jobId in jobIds)
+          AuthorizationManager.AuthorizeForJob(jobId, DT.Permission.Full);
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("UpdateJobStates")) {
         var jobDao = pm.JobDao;
-        pm.UseTransaction(() => {
-          // child task will be deleted by db-trigger
-          jobDao.Delete(jobId);
-          pm.SubmitChanges();
-        });
+        var projectDao = pm.ProjectDao;
+        pm.UseTransaction(() => {
+          foreach (var jobId in jobIds) {
+            var job = jobDao.GetById(jobId);
+            if (job != null) {
+
+              // note: allow solely state changes from "Online" to "StatisticsPending" = deletion request by user for HiveStatisticGenerator
+              var jobStateEntity = jobState.ToEntity();
+              if (job.State == DA.JobState.Online && jobStateEntity == DA.JobState.StatisticsPending) {
+                job.State = jobStateEntity;
+                foreach (var task in job.Tasks
+                .Where(x => x.State == DA.TaskState.Waiting
+                  || x.State == DA.TaskState.Paused
+                  || x.State == DA.TaskState.Offline)) {
+                  task.State = DA.TaskState.Aborted;
+                }
+                pm.SubmitChanges();
+              }              
+            }
+          }
+        });
+      }
+    }
+
+    public IEnumerable<DT.AssignedJobResource> GetAssignedResourcesForJob(Guid jobId) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      AuthorizationManager.AuthorizeForJob(jobId, DT.Permission.Full);
+      var pm = PersistenceManager;
+      var assignedJobResourceDao = pm.AssignedJobResourceDao;
+      using (new PerformanceLogger("GetAssignedResourcesForProject")) {
+        return pm.UseTransaction(() =>
+          assignedJobResourceDao.GetByJobId(jobId)
+          .Select(x => x.ToDto())
+          .ToList()
+        );
       }
     }
@@ -532,6 +700,5 @@
           result = HeartbeatManager.ProcessHeartbeat(heartbeat);
         }
-      }
-      catch (Exception ex) {
+      } catch (Exception ex) {
         DA.LogFactory.GetLogger(this.GetType().Namespace).Log(string.Format("Exception processing Heartbeat: {0}", ex));
       }
@@ -604,15 +771,357 @@
     #endregion
 
-    #region ResourcePermission Methods
-    public void GrantResourcePermissions(Guid resourceId, Guid[] grantedUserIds) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      var pm = PersistenceManager;
-      using (new PerformanceLogger("GrantResourcePermissions")) {
-        pm.UseTransaction(() => {
-          var resource = AuthorizeForResource(pm, resourceId);
-          var resourcePermissions = resource.ResourcePermissions.ToList();
+    #region Project Methods
+    public Guid AddProject(DT.Project projectDto) {
+      if (projectDto == null || projectDto.Id != Guid.Empty) return Guid.Empty;
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      // check if current (non-admin) user is owner of one of projectDto's-parents
+      // note: non-admin users are not allowed to administer root projects (i.e. projects without parental entry)
+      bool isAdmin = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      if (!isAdmin) {
+        if (projectDto != null && projectDto.ParentProjectId.HasValue) {
+          AuthorizationManager.AuthorizeForProjectAdministration(projectDto.ParentProjectId.Value, false);
+        } else {
+          throw new SecurityException(NOT_AUTHORIZED_USERPROJECT);
+        }
+      }
+
+      // check that non-admins can not be set as owner of root projects
+      if (projectDto != null && !projectDto.ParentProjectId.HasValue) {
+        var owner = UserManager.GetUserById(projectDto.OwnerUserId);
+        if (owner == null || !RoleVerifier.IsUserInRole(owner.UserName, HiveRoles.Administrator)) {
+          throw new SecurityException(NOT_AUTHORIZED_PROJECTOWNER);
+        }
+      }
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("AddProject")) {
+        var projectDao = pm.ProjectDao;
+
+        return pm.UseTransaction(() => {
+          var project = projectDao.Save(projectDto.ToEntity());
+
+          var parentProjects = projectDao.GetParentProjectsById(project.ProjectId).ToList();
+          bool isParent = parentProjects.Select(x => x.OwnerUserId == UserManager.CurrentUserId).Any();
+
+          // if user is no admin, but owner of a parent project
+          // check start/end date time boundaries of parent projects before updating child project
+          if (!isAdmin) {
+            var parentProject = parentProjects.Where(x => x.ProjectId == project.ParentProjectId).FirstOrDefault();
+            if (parentProject != null) {
+              if (project.StartDate < parentProject.StartDate) project.StartDate = parentProject.StartDate;
+              if ((parentProject.EndDate.HasValue && project.EndDate.HasValue && project.EndDate > parentProject.EndDate)
+              || (parentProject.EndDate.HasValue && !project.EndDate.HasValue))
+                project.EndDate = parentProject.EndDate;
+            }
+          }
+
+
+          project.ProjectPermissions.Clear();
+          project.ProjectPermissions.Add(new DA.ProjectPermission {
+            GrantedUserId = project.OwnerUserId,
+            GrantedByUserId = UserManager.CurrentUserId
+          });
+
+          pm.SubmitChanges();
+          return project.ProjectId;
+        });
+      }
+    }
+
+    public void UpdateProject(DT.Project projectDto) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      // check if current (non-admin) user is owner of the project or the projectDto's-parents
+      // note: non-admin users are not allowed to administer root projects (i.e. projects without parental entry)
+      bool isAdmin = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      if (!isAdmin) {
+        if (projectDto != null && projectDto.ParentProjectId.HasValue) {
+          AuthorizationManager.AuthorizeForProjectAdministration(projectDto.Id, false);
+        } else {
+          throw new SecurityException(NOT_AUTHORIZED_USERPROJECT);
+        }
+      }
+
+      // check that non-admins can not be set as owner of root projects
+      if (projectDto != null && !projectDto.ParentProjectId.HasValue) {
+        var owner = UserManager.GetUserById(projectDto.OwnerUserId);
+        if (owner == null || !RoleVerifier.IsUserInRole(owner.UserName, HiveRoles.Administrator)) {
+          throw new SecurityException(NOT_AUTHORIZED_PROJECTOWNER);
+        }
+      }
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("UpdateProject")) {
+        var projectDao = pm.ProjectDao;
+        var assignedJobResourceDao = pm.AssignedJobResourceDao;
+        pm.UseTransaction(() => {
+          var project = projectDao.GetById(projectDto.Id);
+          if (project != null) { // (1) update existent project
+            var parentProjects = projectDao.GetParentProjectsById(project.ProjectId).ToList();
+            bool isParent = parentProjects.Select(x => x.OwnerUserId == UserManager.CurrentUserId).Any();
+
+            var formerOwnerId = project.OwnerUserId;
+            var formerStartDate = project.StartDate;
+            var formerEndDate = project.EndDate;
+            projectDto.CopyToEntity(project);
+
+            // if user is no admin, but owner of parent project(s)
+            // check start/end date time boundaries of parent projects before updating child project
+            if (!isAdmin && isParent) {
+              var parentProject = parentProjects.Where(x => x.ProjectId == project.ParentProjectId).FirstOrDefault();
+              if (parentProject != null) {
+                if (project.StartDate < parentProject.StartDate) project.StartDate = formerStartDate;
+                if ((parentProject.EndDate.HasValue && project.EndDate.HasValue && project.EndDate > parentProject.EndDate)
+                || (parentProject.EndDate.HasValue && !project.EndDate.HasValue))
+                  project.EndDate = formerEndDate;
+              }
+            }
+
+            // if user is admin or owner of parent project(s)
+            if (isAdmin || isParent) {
+              // if owner has changed...
+              if (formerOwnerId != projectDto.OwnerUserId) {
+                // Add permission for new owner if not already done
+                if (!project.ProjectPermissions
+                  .Select(pp => pp.GrantedUserId)
+                  .Contains(projectDto.OwnerUserId)) {
+                  project.ProjectPermissions.Add(new DA.ProjectPermission {
+                    GrantedUserId = projectDto.OwnerUserId,
+                    GrantedByUserId = UserManager.CurrentUserId
+                  });
+                }
+              }
+            } else { // if user is only owner of current project, but no admin and no owner of parent project(s)
+              project.OwnerUserId = formerOwnerId;
+              project.StartDate = formerStartDate;
+              project.EndDate = formerEndDate;
+            }
+
+          } else { // (2) save new project
+            var newProject = projectDao.Save(projectDto.ToEntity());
+
+            var parentProjects = projectDao.GetParentProjectsById(project.ProjectId).ToList();
+            bool isParent = parentProjects.Select(x => x.OwnerUserId == UserManager.CurrentUserId).Any();
+
+            // if user is no admin, but owner of a parent project
+            // check start/end date time boundaries of parent projects before updating child project
+            if (!isAdmin) {
+              var parentProject = parentProjects.Where(x => x.ProjectId == project.ParentProjectId).FirstOrDefault();
+              if (parentProject != null) {
+                if (project.StartDate < parentProject.StartDate) project.StartDate = parentProject.StartDate;
+                if ((parentProject.EndDate.HasValue && project.EndDate.HasValue && project.EndDate > parentProject.EndDate)
+                || (parentProject.EndDate.HasValue && !project.EndDate.HasValue))
+                  project.EndDate = parentProject.EndDate;
+              }
+            }
+
+            newProject.ProjectPermissions.Clear();
+            newProject.ProjectPermissions.Add(new DA.ProjectPermission {
+              GrantedUserId = projectDto.OwnerUserId,
+              GrantedByUserId = UserManager.CurrentUserId
+            });
+          }
+
+          pm.SubmitChanges();
+        });
+      }
+    }
+
+    public void DeleteProject(Guid projectId) {
+      if (projectId == Guid.Empty) return;
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      // check if current (non-admin) user is owner of one of the projectDto's-parents
+      // note: non-admin users are not allowed to administer root projects (i.e. projects without parental entry)
+      if (!RoleVerifier.IsInRole(HiveRoles.Administrator)) {
+        AuthorizationManager.AuthorizeForProjectAdministration(projectId, true);
+      }
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("DeleteProject")) {
+        var projectDao = pm.ProjectDao;
+        var jobDao = pm.JobDao;
+        var assignedJobResourceDao = pm.AssignedJobResourceDao;
+        pm.UseTransaction(() => {
+          var projectIds = new HashSet<Guid> { projectId };
+          projectIds.Union(projectDao.GetChildProjectIdsById(projectId));
+
+          var jobs = jobDao.GetByProjectIds(projectIds)
+            .Select(x => x.ToDto())
+            .ToList();
+
+          if (jobs.Count > 0) {
+            throw new InvalidOperationException("There are " + jobs.Count + " job(s) using this project and/or child-projects. It is necessary to delete them before the project.");
+          } else {
+            assignedJobResourceDao.DeleteByProjectIds(projectIds);
+            projectDao.DeleteByIds(projectIds);
+            pm.SubmitChanges();
+          }
+        });
+      }
+    }
+
+    // query granted project for use (i.e. to calculate on)
+    public DT.Project GetProject(Guid projectId) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetProject")) {
+        var projectDao = pm.ProjectDao;
+        var currentUserId = UserManager.CurrentUserId;
+        var userAndGroupIds = new List<Guid> { currentUserId };
+        userAndGroupIds.AddRange(UserManager.GetUserGroupIdsOfUser(currentUserId));
+        return pm.UseTransaction(() => {
+          return projectDao.GetUsageGrantedProjectsForUser(userAndGroupIds)
+          .Where(x => x.ProjectId == projectId)
+          .Select(x => x.ToDto())
+          .SingleOrDefault();
+        });
+      }
+    }
+
+    // query granted projects for use (i.e. to calculate on)
+    public IEnumerable<DT.Project> GetProjects() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetProjects")) {
+        var projectDao = pm.ProjectDao;
+        var currentUserId = UserManager.CurrentUserId;
+        var userAndGroupIds = new List<Guid> { currentUserId };
+        userAndGroupIds.AddRange(UserManager.GetUserGroupIdsOfUser(currentUserId));
+        return pm.UseTransaction(() => {
+          var projects = projectDao.GetUsageGrantedProjectsForUser(userAndGroupIds)
+            .Select(x => x.ToDto()).ToList();
+          var now = DateTime.Now;
+          return projects.Where(x => x.StartDate <= now && (x.EndDate == null || x.EndDate >= now)).ToList();
+        });
+      }
+    }
+
+    public IEnumerable<DT.Project> GetProjectsForAdministration() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetProjectsForAdministration")) {
+        var projectDao = pm.ProjectDao;
+
+        return pm.UseTransaction(() => {
+          if (isAdministrator) {
+            return projectDao.GetAll().Select(x => x.ToDto()).ToList();
+          } else {
+            var currentUserId = UserManager.CurrentUserId;
+            return projectDao.GetAdministrationGrantedProjectsForUser(currentUserId)
+              .Select(x => x.ToDto()).ToList();
+
+          }
+        });
+      }
+    }
+
+    public IDictionary<Guid, HashSet<Guid>> GetProjectGenealogy() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetProjectGenealogy")) {
+        var projectDao = pm.ProjectDao;
+        var projectAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        return pm.UseTransaction(() => {
+          var projects = projectDao.GetAll().ToList();
+          projects.ForEach(p => projectAncestors.Add(p.ProjectId, new HashSet<Guid>()));
+          foreach (var p in projects) {
+            var parentProject = p.ParentProject;
+            while (parentProject != null) {
+              projectAncestors[p.ProjectId].Add(parentProject.ProjectId);
+              parentProject = parentProject.ParentProject;
+            }
+          }
+          return projectAncestors;
+        });
+      }
+    }
+
+    public IDictionary<Guid, string> GetProjectNames() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetProjectNames")) {
+        var projectDao = pm.ProjectDao;
+        var projectNames = new Dictionary<Guid, string>();
+        return pm.UseTransaction(() => {
+          projectDao
+            .GetAll().ToList()
+            .ForEach(p => projectNames.Add(p.ProjectId, p.Name));
+          return projectNames;
+        });
+      }
+    }
+    #endregion
+
+    #region ProjectPermission Methods
+    public void SaveProjectPermissions(Guid projectId, List<Guid> grantedUserIds, bool reassign, bool cascading, bool reassignCascading) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      if (projectId == null || grantedUserIds == null) return;
+      AuthorizationManager.AuthorizeForProjectAdministration(projectId, false);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("SaveProjectPermissions")) {
+        var projectDao = pm.ProjectDao;
+        var projectPermissionDao = pm.ProjectPermissionDao;
+        var assignedJobResourceDao = pm.AssignedJobResourceDao;
+
+        pm.UseTransaction(() => {
+          var project = projectDao.GetById(projectId);
+          if (project == null) return;
+          var projectPermissions = project.ProjectPermissions.Select(x => x.GrantedUserId).ToArray();
+
+          // guarantee that project owner is always permitted
+          if (!grantedUserIds.Contains(project.OwnerUserId)) {
+            grantedUserIds.Add(project.OwnerUserId);
+          }
+
+          //var addedPermissions = grantedUserIds.Except(projectPermissions);
+          var removedPermissions = projectPermissions.Except(grantedUserIds);
+
+          // remove job assignments and project permissions
+          if (reassign) {
+            assignedJobResourceDao.DeleteByProjectId(project.ProjectId);
+            project.ProjectPermissions.Clear();
+          } else {
+
+            var ugt = GetUserGroupTree();
+            var permittedGuids = new HashSet<Guid>(); // User- and Group-Guids
+            var notpermittedGuids = new HashSet<Guid>();
+
+            // remove job assignments:
+            // (1) get all member-Guids of all still or fresh permitted user/groups
+            foreach (var item in grantedUserIds) {
+              permittedGuids.Add(item);
+              if (ugt.ContainsKey(item)) {
+                ugt[item].ToList().ForEach(x => permittedGuids.Add(x));
+              }
+            }
+
+            // (2) get all member-Guids of users and groups in removedPermissions
+            foreach (var item in removedPermissions) {
+              notpermittedGuids.Add(item);
+              if (ugt.ContainsKey(item)) {
+                ugt[item].ToList().ForEach(x => notpermittedGuids.Add(x));
+              }
+            }
+
+            // (3) get all Guids which are in removedPermissions but not in grantedUserIds
+            var definitelyNotPermittedGuids = notpermittedGuids.Except(permittedGuids);
+
+            // (4) delete jobs of those
+            assignedJobResourceDao.DeleteByProjectIdAndUserIds(project.ProjectId, definitelyNotPermittedGuids);
+
+
+            // remove project permissions
+            foreach (var item in project.ProjectPermissions
+              .Where(x => removedPermissions.Contains(x.GrantedUserId))
+              .ToList()) {
+              project.ProjectPermissions.Remove(item);
+            }
+          }
+          pm.SubmitChanges();
+
+          // add project permissions
           foreach (var id in grantedUserIds) {
-            if (resourcePermissions.All(x => x.GrantedUserId != id)) {
-              resource.ResourcePermissions.Add(new DA.ResourcePermission {
+            if (project.ProjectPermissions.All(x => x.GrantedUserId != id)) {
+              project.ProjectPermissions.Add(new DA.ProjectPermission {
                 GrantedUserId = id,
                 GrantedByUserId = UserManager.CurrentUserId
@@ -621,27 +1130,94 @@
           }
           pm.SubmitChanges();
-        });
-      }
-    }
-
-    public void RevokeResourcePermissions(Guid resourceId, Guid[] grantedUserIds) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      var pm = PersistenceManager;
-      using (new PerformanceLogger("RevokeResourcePermissions")) {
-        var resourcePermissionDao = pm.ResourcePermissionDao;
-        pm.UseTransaction(() => {
-          AuthorizeForResource(pm, resourceId);
-          resourcePermissionDao.DeleteByResourceAndGrantedUserId(resourceId, grantedUserIds);
-          pm.SubmitChanges();
-        });
-      }
-    }
-
-    public IEnumerable<DT.ResourcePermission> GetResourcePermissions(Guid resourceId) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      var pm = PersistenceManager;
-      using (new PerformanceLogger("GetResourcePermissions")) {
-        var resourcePermissionDao = pm.ResourcePermissionDao;
-        return pm.UseTransaction(() => resourcePermissionDao.GetByResourceId(resourceId)
+
+          if (cascading) {
+            var childProjects = projectDao.GetChildProjectsById(projectId).ToList();
+            var childProjectIds = childProjects.Select(x => x.ProjectId).ToList();
+
+            // remove job assignments
+            if (reassignCascading) {
+              assignedJobResourceDao.DeleteByProjectIds(childProjectIds);
+            } else {
+              assignedJobResourceDao.DeleteByProjectIdsAndUserIds(childProjectIds, removedPermissions);
+            }
+
+            foreach (var p in childProjects) {
+              var cpAssignedPermissions = p.ProjectPermissions.Select(x => x.GrantedUserId).ToList();
+              // guarantee that project owner is always permitted
+              if (!cpAssignedPermissions.Contains(p.OwnerUserId)) {
+                cpAssignedPermissions.Add(p.OwnerUserId);
+              }
+              var cpRemovedPermissions = cpAssignedPermissions.Where(x => x != p.OwnerUserId).Except(grantedUserIds);
+
+              // remove left-over job assignments (for non-reassignments)
+              if (!reassignCascading) {
+                assignedJobResourceDao.DeleteByProjectIdAndUserIds(p.ProjectId, cpRemovedPermissions);
+              }
+
+              // remove project permissions
+              if (reassignCascading) {
+                p.ProjectPermissions.Clear();
+              } else {
+                foreach (var item in p.ProjectPermissions
+                  .Where(x => x.GrantedUserId != p.OwnerUserId
+                    && (removedPermissions.Contains(x.GrantedUserId) || cpRemovedPermissions.Contains(x.GrantedUserId)))
+                  .ToList()) {
+                  p.ProjectPermissions.Remove(item);
+                }
+              }
+              pm.SubmitChanges();
+
+              // add project permissions
+              var cpGrantedUserIds = new HashSet<Guid>(grantedUserIds);
+              cpGrantedUserIds.Add(p.OwnerUserId);
+
+              foreach (var id in cpGrantedUserIds) {
+                if (p.ProjectPermissions.All(x => x.GrantedUserId != id)) {
+                  p.ProjectPermissions.Add(new DA.ProjectPermission {
+                    GrantedUserId = id,
+                    GrantedByUserId = UserManager.CurrentUserId
+                  });
+                }
+              }
+            }
+          }
+          pm.SubmitChanges();
+        });
+      }
+    }
+
+    //private void GrantProjectPermissions(Guid projectId, List<Guid> grantedUserIds, bool cascading) {
+    //  throw new NotImplementedException();
+    //}
+
+    //private void RevokeProjectPermissions(Guid projectId, List<Guid> grantedUserIds, bool cascading) {
+    //  RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+    //  if (projectId == null || grantedUserIds == null || !grantedUserIds.Any()) return;
+    //  AuthorizationManager.AuthorizeForProjectAdministration(projectId, false);
+    //  var pm = PersistenceManager;
+    //  using (new PerformanceLogger("RevokeProjectPermissions")) {
+    //    var projectPermissionDao = pm.ProjectPermissionDao;
+    //    var projectDao = pm.ProjectDao;
+    //    var assignedJobResourceDao = pm.AssignedJobResourceDao;
+    //    pm.UseTransaction(() => {
+    //      if (cascading) {
+    //        var childProjectIds = projectDao.GetChildProjectIdsById(projectId).ToList();
+    //        projectPermissionDao.DeleteByProjectIdsAndGrantedUserIds(childProjectIds, grantedUserIds);
+    //        assignedJobResourceDao.DeleteByProjectIdsAndUserIds(childProjectIds, grantedUserIds);
+    //      }
+    //      projectPermissionDao.DeleteByProjectIdAndGrantedUserIds(projectId, grantedUserIds);
+    //      assignedJobResourceDao.DeleteByProjectIdAndUserIds(projectId, grantedUserIds);
+    //      pm.SubmitChanges();
+    //    });
+    //  }
+    //}
+
+    public IEnumerable<DT.ProjectPermission> GetProjectPermissions(Guid projectId) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      AuthorizationManager.AuthorizeForProjectAdministration(projectId, false);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetProjectPermissions")) {
+        var projectPermissionDao = pm.ProjectPermissionDao;
+        return pm.UseTransaction(() => projectPermissionDao.GetByProjectId(projectId)
           .Select(x => x.ToDto())
           .ToList()
@@ -651,4 +1227,176 @@
     #endregion
 
+    #region AssignedProjectResource Methods
+    // basic: remove and add assignments (resourceIds) to projectId and its depending jobs
+    // reassign: clear all assignments from project and its depending jobs, before adding new ones (resourceIds)
+    // cascading: "basic" mode for child-projects
+    // reassignCascading: "reassign" mode for child-projects
+    public void SaveProjectResourceAssignments(Guid projectId, List<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      if (projectId == null || resourceIds == null) return;
+      AuthorizationManager.AuthorizeForProjectResourceAdministration(projectId, resourceIds);
+      bool isAdmin = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("SaveProjectResourceAssignments")) {
+        var projectDao = pm.ProjectDao;
+        var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+        var assignedJobResourceDao = pm.AssignedJobResourceDao;
+        pm.UseTransaction(() => {
+          var project = projectDao.GetById(projectId);
+
+          var parentProjects = projectDao.GetParentProjectsById(project.ProjectId).ToList();
+          bool isParent = parentProjects.Select(x => x.OwnerUserId == UserManager.CurrentUserId).Any();
+
+          var assignedResources = project.AssignedProjectResources.Select(x => x.ResourceId).ToArray();
+          if (!isParent && !isAdmin) resourceIds = assignedResources.ToList();
+          var removedAssignments = assignedResources.Except(resourceIds);
+
+          // if user is admin or owner of parent project(s)
+          if (isAdmin || isParent) {
+            // remove job and project assignments
+            if (reassign) {
+              assignedJobResourceDao.DeleteByProjectId(project.ProjectId);
+              project.AssignedProjectResources.Clear();
+            } else {
+              assignedJobResourceDao.DeleteByProjectIdAndResourceIds(projectId, removedAssignments);
+              foreach (var item in project.AssignedProjectResources
+                .Where(x => removedAssignments.Contains(x.ResourceId))
+                .ToList()) {
+                project.AssignedProjectResources.Remove(item);
+              }
+            }
+            pm.SubmitChanges();
+
+            // add project assignments
+            foreach (var id in resourceIds) {
+              if (project.AssignedProjectResources.All(x => x.ResourceId != id)) {
+                project.AssignedProjectResources.Add(new DA.AssignedProjectResource {
+                  ResourceId = id
+                });
+              }
+            }
+            pm.SubmitChanges();
+          }
+
+          // if user is admin, project owner or owner of parent projects
+          if (cascading) {
+            var childProjects = projectDao.GetChildProjectsById(projectId).ToList();
+            var childProjectIds = childProjects.Select(x => x.ProjectId).ToList();
+
+            // remove job assignments
+            if (reassignCascading) {
+              assignedJobResourceDao.DeleteByProjectIds(childProjectIds);
+            } else {
+              assignedJobResourceDao.DeleteByProjectIdsAndResourceIds(childProjectIds, removedAssignments);
+            }
+            foreach (var p in childProjects) {
+              var cpAssignedResources = p.AssignedProjectResources.Select(x => x.ResourceId).ToArray();
+              var cpRemovedAssignments = cpAssignedResources.Except(resourceIds);
+
+              // remove left-over job assignments (for non-reassignments)
+              if (!reassignCascading) {
+                assignedJobResourceDao.DeleteByProjectIdAndResourceIds(p.ProjectId, cpRemovedAssignments);
+              }
+
+              // remove project assignments
+              if (reassignCascading) {
+                p.AssignedProjectResources.Clear();
+              } else {
+                foreach (var item in p.AssignedProjectResources
+                  .Where(x => removedAssignments.Contains(x.ResourceId) || cpRemovedAssignments.Contains(x.ResourceId))
+                  .ToList()) {
+                  p.AssignedProjectResources.Remove(item);
+                }
+              }
+              pm.SubmitChanges();
+
+              // add project assignments
+              foreach (var id in resourceIds) {
+                if (p.AssignedProjectResources.All(x => x.ResourceId != id)) {
+                  p.AssignedProjectResources.Add(new DA.AssignedProjectResource {
+                    ResourceId = id
+                  });
+                }
+              }
+            }
+          }
+          pm.SubmitChanges();
+        });
+      }
+    }
+
+    //private void AssignProjectResources(Guid projectId, List<Guid> resourceIds, bool cascading) {
+    //  throw new NotImplementedException();
+    //}
+
+    //private void UnassignProjectResources(Guid projectId, List<Guid> resourceIds, bool cascading) {
+    //  RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+    //  if (projectId == null || resourceIds == null || !resourceIds.Any()) return;
+    //  AuthorizationManager.AuthorizeForProjectResourceAdministration(projectId, resourceIds);
+    //  var pm = PersistenceManager;
+    //  using (new PerformanceLogger("UnassignProjectResources")) {
+    //    var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+    //    var assignedJobResourceDao = pm.AssignedJobResourceDao;
+    //    var projectDao = pm.ProjectDao;
+    //    pm.UseTransaction(() => {
+    //      if (cascading) {
+    //        var childProjectIds = projectDao.GetChildProjectIdsById(projectId).ToList();
+    //        assignedProjectResourceDao.DeleteByProjectIdsAndResourceIds(childProjectIds, resourceIds);
+    //        assignedJobResourceDao.DeleteByProjectIdsAndResourceIds(childProjectIds, resourceIds);
+    //      }
+    //      assignedProjectResourceDao.DeleteByProjectIdAndResourceIds(projectId, resourceIds);
+    //      assignedJobResourceDao.DeleteByProjectIdAndResourceIds(projectId, resourceIds);
+    //      pm.SubmitChanges();
+    //    });
+    //  }
+    //}
+
+    public IEnumerable<DT.AssignedProjectResource> GetAssignedResourcesForProject(Guid projectId) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      AuthorizationManager.AuthorizeUserForProjectUse(UserManager.CurrentUserId, projectId);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetAssignedResourcesForProject")) {
+        var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+        return pm.UseTransaction(() => assignedProjectResourceDao.GetByProjectId(projectId)
+          .Select(x => x.ToDto())
+          .ToList()
+        );
+      }
+    }
+
+    public IEnumerable<DT.AssignedProjectResource> GetAssignedResourcesForProjectAdministration(Guid projectId) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      AuthorizationManager.AuthorizeForProjectAdministration(projectId, false);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetAssignedResourcesForProject")) {
+        var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+        return pm.UseTransaction(() => assignedProjectResourceDao.GetByProjectId(projectId)
+          .Select(x => x.ToDto())
+          .ToList()
+        );
+      }
+    }
+
+    public IEnumerable<DT.AssignedProjectResource> GetAssignedResourcesForProjectsAdministration(IEnumerable<Guid> projectIds) {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      foreach (var id in projectIds)
+        AuthorizationManager.AuthorizeForProjectAdministration(id, false);
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetAssignedResourcesForProject")) {
+        var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+        var assignments = new List<DT.AssignedProjectResource>();
+        pm.UseTransaction(() => {
+          foreach (var id in projectIds) {
+            assignments.AddRange(assignedProjectResourceDao.GetByProjectId(id)
+              .Select(x => x.ToDto()));
+          }
+        });
+        return assignments.Distinct();
+      }
+    }
+
+    #endregion
+
     #region Slave Methods
     public Guid AddSlave(DT.Slave slaveDto) {
@@ -666,5 +1414,5 @@
 
     public Guid AddSlaveGroup(DT.SlaveGroup slaveGroupDto) {
-      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator);
       var pm = PersistenceManager;
       using (new PerformanceLogger("AddSlaveGroup")) {
@@ -690,50 +1438,169 @@
     }
 
+    // query granted slaves for use (i.e. to calculate on)
     public IEnumerable<DT.Slave> GetSlaves() {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
       var pm = PersistenceManager;
       using (new PerformanceLogger("GetSlaves")) {
         var slaveDao = pm.SlaveDao;
-        var resourcePermissionDao = pm.ResourcePermissionDao;
+        var projectDao = pm.ProjectDao;
+        var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+
+        // collect user information
         var currentUserId = UserManager.CurrentUserId;
-        return pm.UseTransaction(() => {
-          var resourcePermissions = resourcePermissionDao.GetAll();
-          return slaveDao.GetAll().ToList()
-            .Where(x => isAdministrator
-              || x.OwnerUserId == null
-              || x.OwnerUserId == currentUserId
-              || UserManager.VerifyUser(currentUserId, resourcePermissions
-                  .Where(y => y.ResourceId == x.ResourceId)
-                  .Select(z => z.GrantedUserId)
-                  .ToList())
-              )
+        var userAndGroupIds = new List<Guid> { currentUserId };
+        userAndGroupIds.AddRange(UserManager.GetUserGroupIdsOfUser(currentUserId));
+
+        return pm.UseTransaction(() => {
+          var slaves = slaveDao.GetAll()
             .Select(x => x.ToDto())
             .ToList();
-        });
-      }
-    }
-
+          var grantedProjectIds = projectDao.GetUsageGrantedProjectsForUser(userAndGroupIds)
+            .Select(x => x.ProjectId)
+            .ToList();
+          var grantedResourceIds = assignedProjectResourceDao.GetAllGrantedResourcesByProjectIds(grantedProjectIds)
+            .Select(x => x.ResourceId)
+            .ToList();
+
+          return slaves
+            .Where(x => grantedResourceIds.Contains(x.Id))
+            .ToList();
+        });
+      }
+    }
+
+    // query granted slave groups for use (i.e. to calculate on)
     public IEnumerable<DT.SlaveGroup> GetSlaveGroups() {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
-      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
       var pm = PersistenceManager;
       using (new PerformanceLogger("GetSlaveGroups")) {
         var slaveGroupDao = pm.SlaveGroupDao;
-        var resourcePermissionDao = pm.ResourcePermissionDao;
+        var projectDao = pm.ProjectDao;
+        var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+
+        // collect user information
         var currentUserId = UserManager.CurrentUserId;
-        return pm.UseTransaction(() => {
-          var resourcePermissions = resourcePermissionDao.GetAll();
-          return slaveGroupDao.GetAll().ToList()
-            .Where(x => isAdministrator
-              || x.OwnerUserId == null
-              || x.OwnerUserId == currentUserId
-              || UserManager.VerifyUser(currentUserId, resourcePermissions
-                  .Where(y => y.ResourceId == x.ResourceId)
-                  .Select(z => z.GrantedUserId)
-                  .ToList())
-              )
+        var userAndGroupIds = new List<Guid> { currentUserId };
+        userAndGroupIds.AddRange(UserManager.GetUserGroupIdsOfUser(currentUserId));
+
+        return pm.UseTransaction(() => {
+          var slaveGroups = slaveGroupDao.GetAll()
             .Select(x => x.ToDto())
             .ToList();
+          var grantedProjectIds = projectDao.GetUsageGrantedProjectsForUser(userAndGroupIds)
+            .Select(x => x.ProjectId)
+            .ToList();
+          var grantedResourceIds = assignedProjectResourceDao.GetAllGrantedResourcesByProjectIds(grantedProjectIds)
+            .Select(x => x.ResourceId)
+            .ToList();
+
+          return slaveGroups
+            .Where(x => grantedResourceIds.Contains(x.Id))
+            .ToList();
+        });
+      }
+    }
+
+    // query granted slaves for resource administration
+    public IEnumerable<DT.Slave> GetSlavesForAdministration() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetSlavesForAdministration")) {
+        var slaveDao = pm.SlaveDao;
+        var currentUserId = UserManager.CurrentUserId;
+
+        if (isAdministrator) {
+          return pm.UseTransaction(() => {
+            return slaveDao.GetAll()
+              .Select(x => x.ToDto())
+              .ToList();
+          });
+        } else {
+          var slaves = slaveDao.GetAll()
+            .Select(x => x.ToDto())
+            .ToList();
+          var projectDao = pm.ProjectDao;
+          var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+          var projects = projectDao.GetAdministrationGrantedProjectsForUser(currentUserId).ToList();
+          var resourceIds = assignedProjectResourceDao
+            .GetAllGrantedResourcesByProjectIds(projects.Select(x => x.ProjectId).ToList())
+            .Select(x => x.ResourceId)
+            .ToList();
+
+          return slaves
+            .Where(x => resourceIds.Contains(x.Id))
+            .ToList();
+        }
+      }
+    }
+
+    // query granted slave groups for resource administration
+    public IEnumerable<DT.SlaveGroup> GetSlaveGroupsForAdministration() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetSlaveGroupsForAdministration")) {
+        var slaveGroupDao = pm.SlaveGroupDao;
+        var currentUserId = UserManager.CurrentUserId;
+
+        if (isAdministrator) {
+          return pm.UseTransaction(() => {
+            return slaveGroupDao.GetAll()
+              .Select(x => x.ToDto())
+              .ToList();
+          });
+        } else {
+          var slaveGroups = slaveGroupDao.GetAll()
+            .Select(x => x.ToDto())
+            .ToList();
+          var projectDao = pm.ProjectDao;
+          var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+          var projects = projectDao.GetAdministrationGrantedProjectsForUser(currentUserId).ToList();
+          var resourceIds = assignedProjectResourceDao
+            .GetAllGrantedResourcesByProjectIds(projects.Select(x => x.ProjectId).ToList())
+            .Select(x => x.ResourceId)
+            .ToList();
+
+          return slaveGroups
+            .Where(x => resourceIds.Contains(x.Id))
+            .ToList();
+        }
+      }
+    }
+
+    public IDictionary<Guid, HashSet<Guid>> GetResourceGenealogy() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetResourceGenealogy")) {
+        var resourceDao = pm.ResourceDao;
+        var resourceAncestors = new Dictionary<Guid, HashSet<Guid>>();
+        return pm.UseTransaction(() => {
+          var resources = resourceDao.GetAll().ToList();
+          resources.ForEach(r => resourceAncestors.Add(r.ResourceId, new HashSet<Guid>()));
+
+          foreach (var r in resources) {
+            var parentResource = r.ParentResource;
+            while (parentResource != null) {
+              resourceAncestors[r.ResourceId].Add(parentResource.ResourceId);
+              parentResource = parentResource.ParentResource;
+            }
+          }
+          return resourceAncestors;
+        });
+      }
+    }
+
+    public IDictionary<Guid, string> GetResourceNames() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("GetResourceNames")) {
+        var resourceDao = pm.ResourceDao;
+        var resourceNames = new Dictionary<Guid, string>();
+        return pm.UseTransaction(() => {
+          resourceDao
+            .GetAll().ToList()
+            .ForEach(p => resourceNames.Add(p.ResourceId, p.Name));
+          return resourceNames;
         });
       }
@@ -742,4 +1609,6 @@
     public void UpdateSlave(DT.Slave slaveDto) {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      if (slaveDto == null) return;
+      AuthorizationManager.AuthorizeForResourceAdministration(slaveDto.Id);
       var pm = PersistenceManager;
       using (new PerformanceLogger("UpdateSlave")) {
@@ -759,4 +1628,6 @@
     public void UpdateSlaveGroup(DT.SlaveGroup slaveGroupDto) {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      if (slaveGroupDto == null) return;
+      AuthorizationManager.AuthorizeForResourceAdministration(slaveGroupDto.Id);
       var pm = PersistenceManager;
       using (new PerformanceLogger("UpdateSlaveGroup")) {
@@ -775,4 +1646,5 @@
 
     public void DeleteSlave(Guid slaveId) {
+      if (slaveId == Guid.Empty) return;
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
       AuthorizationManager.AuthorizeForResourceAdministration(slaveId);
@@ -788,11 +1660,14 @@
 
     public void DeleteSlaveGroup(Guid slaveGroupId) {
+      if (slaveGroupId == Guid.Empty) return;
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
       AuthorizationManager.AuthorizeForResourceAdministration(slaveGroupId);
       var pm = PersistenceManager;
       using (new PerformanceLogger("DeleteSlaveGroup")) {
-        var slaveGroupDao = pm.SlaveGroupDao;
-        pm.UseTransaction(() => {
-          slaveGroupDao.Delete(slaveGroupId);
+        var resourceDao = pm.ResourceDao;
+        pm.UseTransaction(() => {
+          var resourceIds = new HashSet<Guid> { slaveGroupId };
+          resourceIds.Union(resourceDao.GetChildResourceIdsById(slaveGroupId));
+          resourceDao.DeleteByIds(resourceIds);
           pm.SubmitChanges();
         });
@@ -946,4 +1821,35 @@
       var user = ServiceLocator.Instance.UserManager.GetUserByName(username);
       return user != null ? (Guid?)user.ProviderUserKey ?? Guid.Empty : Guid.Empty;
+    }
+
+    public Dictionary<Guid, HashSet<Guid>> GetUserGroupTree() {
+      var userGroupTree = new Dictionary<Guid, HashSet<Guid>>();
+      var userGroupMapping = UserManager.GetUserGroupMapping();
+
+      foreach (var ugm in userGroupMapping) {
+        if (ugm.Parent == null || ugm.Child == null) continue;
+
+        if (!userGroupTree.ContainsKey(ugm.Parent)) {
+          userGroupTree.Add(ugm.Parent, new HashSet<Guid>());
+        }
+        userGroupTree[ugm.Parent].Add(ugm.Child);
+      }
+
+      return userGroupTree;
+    }
+
+    public bool CheckAccessToAdminAreaGranted() {
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("CheckAccessToAdminAreaGranted")) {
+        if (isAdministrator) {
+          return true;
+        } else {
+          var projectDao = pm.ProjectDao;
+          var currentUserId = UserManager.CurrentUserId;
+          return projectDao.GetAdministrationGrantedProjectsForUser(currentUserId).Any();
+        }
+      }
     }
     #endregion
@@ -990,13 +1896,54 @@
     }
 
-    private DA.Resource AuthorizeForResource(IPersistenceManager pm, Guid resourceId) {
-      var resourceDao = pm.ResourceDao;
-      var resource = resourceDao.GetById(resourceId);
-      if (resource == null) throw new SecurityException("Not authorized");
-      if (resource.OwnerUserId != UserManager.CurrentUserId
-          && !RoleVerifier.IsInRole(HiveRoles.Administrator)) {
-        throw new SecurityException("Not authorized");
-      }
-      return resource;
+    private void CheckProjectAvailability(IPersistenceManager pm, Guid projectId, DateTime date) {
+      var projectDao = pm.ProjectDao;
+      using (new PerformanceLogger("UpdateJob")) {
+        var project = pm.UseTransaction(() => {
+          return projectDao.GetById(projectId);
+        });
+        if (project != null) {
+          if (project.StartDate > date) throw new ArgumentException("Cannot add job to specified project. The start date of the project is still in the future.");
+          else if (project.EndDate != null && project.EndDate < date) throw new ArgumentException("Cannot add job to specified project. The end date of the project is already reached.");
+        } else {
+          throw new ArgumentException("Cannot add job to specified project. The project seems not to be available anymore.");
+        }
+      }
+    }
+
+    private void EvaluateJobs(IPersistenceManager pm, IEnumerable<DT.Job> jobs) {
+      if (jobs == null || !jobs.Any()) return;
+
+      var currentUserId = UserManager.CurrentUserId;
+      var taskDao = pm.TaskDao;
+      var jobPermissionDao = pm.JobPermissionDao;
+
+      var statistics = taskDao.GetAll()
+        .Where(x => jobs.Select(y => y.Id).Contains(x.JobId))
+        .GroupBy(x => x.JobId)
+        .Select(x => new {
+          x.Key,
+          TotalCount = x.Count(),
+          CalculatingCount = x.Count(y => y.State == DA.TaskState.Calculating),
+          FinishedCount = x.Count(y => CompletedStates.Contains(y.State))
+        })
+        .ToList();
+
+      foreach (var job in jobs) {
+        var statistic = statistics.FirstOrDefault(x => x.Key == job.Id);
+        if (statistic != null) {
+          job.JobCount = statistic.TotalCount;
+          job.CalculatingCount = statistic.CalculatingCount;
+          job.FinishedCount = statistic.FinishedCount;
+        }
+
+        job.OwnerUsername = UserManager.GetUserNameById(job.OwnerUserId);
+
+        if (currentUserId == job.OwnerUserId) {
+          job.Permission = Permission.Full;
+        } else {
+          var jobPermission = jobPermissionDao.GetByJobAndUserId(job.Id, currentUserId);
+          job.Permission = jobPermission == null ? Permission.NotAllowed : jobPermission.Permission.ToDto();
+        }
+      }
     }
     #endregion
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HiveStatisticsGenerator.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HiveStatisticsGenerator.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/HiveStatisticsGenerator.cs	(revision 16240)
@@ -37,6 +37,13 @@
     public void GenerateStatistics() {
       using (var pm = new PersistenceManager()) {
+
+        pm.UseTransaction(() => {
+          UpdateDimProjectTable(pm);
+          pm.SubmitChanges();
+        });
+
         pm.UseTransaction(() => {
           UpdateDimUserTable(pm);
+          
           UpdateDimJobTable(pm);
           UpdateDimClientsTable(pm);
@@ -54,8 +61,10 @@
             UpdateFactClientInfoTable(time, pm);
             pm.SubmitChanges();
+            UpdateFactProjectInfoTable(time, pm);
+            pm.SubmitChanges();
           });
 
           pm.UseTransaction(() => {
-            UpdateTaskFactsTable(pm);
+            UpdateFactTaskTable(pm);
             try {
               pm.SubmitChanges();
@@ -72,4 +81,9 @@
           });
         }
+
+        pm.UseTransaction(() => {
+          FlagJobsForDeletion(pm);
+          pm.SubmitChanges();
+        });
       }
     }
@@ -106,5 +120,67 @@
     }
 
+    // add new projects
+    // delete expired projects
+    // update information of existing projects
+    private void UpdateDimProjectTable(PersistenceManager pm) {
+      var projectDao = pm.ProjectDao;
+      var dimProjectDao = pm.DimProjectDao;
+
+      var projects = projectDao.GetAll().ToList();
+      var dimProjects = dimProjectDao.GetAllOnlineProjects().ToList();
+
+      var onlineProjects = dimProjects.Where(x => projects.Select(y => y.ProjectId).Contains(x.ProjectId));
+      var addedProjects = projects.Where(x => !dimProjects.Select(y => y.ProjectId).Contains(x.ProjectId));
+      var removedProjects = dimProjects.Where(x => !projects.Select(y => y.ProjectId).Contains(x.ProjectId));
+
+      // set expiration time of removed projects
+      foreach (var p in removedProjects) {
+        p.DateExpired = DateTime.Now;
+      }
+
+      // add new projects
+      dimProjectDao.Save(addedProjects.Select(x => new DimProject {
+        ProjectId = x.ProjectId,
+        ParentProjectId = x.ParentProjectId,
+        Name = x.Name,
+        Description = x.Description,
+        OwnerUserId = x.OwnerUserId,
+        StartDate = x.StartDate,
+        EndDate = x.EndDate,
+        DateCreated = x.DateCreated,
+        DateExpired = null
+      }));
+
+      // expire project if its parent has changed and create a new entry
+      // otherwise perform "normal" update
+      foreach (var dimP in onlineProjects) {
+        var p = projects.Where(x => x.ProjectId == dimP.ProjectId).SingleOrDefault();
+        if (p != null) {
+          if (dimP.ParentProjectId == null ? p.ParentProjectId != null : dimP.ParentProjectId != p.ParentProjectId) { // or: (!object.Equals(dimP.ParentProjectId, p.ParentProjectId))
+            dimP.DateExpired = DateTime.Now;
+            dimProjectDao.Save(new DimProject {
+              ProjectId = p.ProjectId,
+              ParentProjectId = p.ParentProjectId,
+              Name = p.Name,
+              Description = p.Description,
+              OwnerUserId = p.OwnerUserId,
+              StartDate = p.StartDate,
+              EndDate = p.EndDate,
+              DateCreated = p.DateCreated,
+              DateExpired = null
+            });
+          } else {
+            dimP.Name = p.Name;
+            dimP.Description = p.Description;
+            dimP.OwnerUserId = p.OwnerUserId;
+            dimP.StartDate = p.StartDate;
+            dimP.EndDate = p.EndDate;
+          }
+        }
+      }
+    }
+
     private void UpdateDimJobTable(PersistenceManager pm) {
+      var dimProjectDao = pm.DimProjectDao;
       var dimJobDao = pm.DimJobDao;
       var jobDao = pm.JobDao;
@@ -118,4 +194,5 @@
           JobName = x.Name ?? string.Empty,
           DateCreated = x.DateCreated,
+          ProjectId = dimProjectDao.GetLastValidIdByProjectId(x.ProjectId),
           TotalTasks = taskDao.GetAll().Count(y => y.JobId == x.JobId)
         })
@@ -127,4 +204,5 @@
         UserName = GetUserName(x.UserId),
         DateCreated = x.DateCreated,
+        ProjectId = x.ProjectId,
         TotalTasks = x.TotalTasks,
         CompletedTasks = 0,
@@ -134,4 +212,5 @@
 
     private void UpdateExistingDimJobs(PersistenceManager pm) {
+      var dimProjectDao = pm.DimProjectDao;
       var jobDao = pm.JobDao;
       var dimJobDao = pm.DimJobDao;
@@ -151,8 +230,9 @@
           }
         }
+        var job = jobDao.GetById(dimJob.JobId);
         if (totalTasks == completedTasks) {
           var completeDate = factTaskDao.GetLastCompletedTaskFromJob(dimJob.JobId);
           if (completeDate == null) {
-            if (jobDao.GetById(dimJob.JobId) == null) {
+            if (job == null) {
               completeDate = DateTime.Now;
             }
@@ -160,4 +240,9 @@
           dimJob.DateCompleted = completeDate;
         }
+        if(job != null) {
+          dimJob.JobName = job.Name;
+          dimJob.ProjectId = dimProjectDao.GetLastValidIdByProjectId(job.ProjectId);
+        }
+
         dimJob.TotalTasks = totalTasks;
         dimJob.CompletedTasks = completedTasks;
@@ -165,53 +250,114 @@
     }
 
+    private void FlagJobsForDeletion(PersistenceManager pm) {
+      var jobDao = pm.JobDao;
+      var jobs = jobDao.GetJobsReadyForDeletion();
+      foreach(var job in jobs) {
+        job.State = JobState.DeletionPending;
+      }
+    }
+
     private void UpdateDimClientsTable(PersistenceManager pm) {
       var dimClientDao = pm.DimClientDao;
-      var slaveDao = pm.SlaveDao;
-      var slaves = slaveDao.GetAll();
-      var recentlyAddedClients = dimClientDao.GetActiveClients();
-      var slaveIds = slaves.Select(x => x.ResourceId);
-
-      var removedClientIds = recentlyAddedClients
-        .Where(x => !slaveIds.Contains(x.ResourceId))
-        .Select(x => x.Id);
-      var modifiedClients =
-        from slave in slaves
-        join client in recentlyAddedClients on slave.ResourceId equals client.ResourceId
-        where (slave.Name != client.Name
-               || slave.ParentResourceId == null && client.ResourceGroupId != null // because both can be null and null comparison
-               || slave.ParentResourceId != null && client.ResourceGroupId == null // does return no entry on the sql server
-               || slave.ParentResourceId != client.ResourceGroupId
-               || ((slave.ParentResource != null) && slave.ParentResource.ParentResourceId != client.ResourceGroup2Id))
-        select new {
-          SlaveId = slave.ResourceId,
-          ClientId = client.Id
-        };
-      var clientIds = dimClientDao.GetActiveClients().Select(x => x.ResourceId);
-      var modifiedClientIds = modifiedClients.Select(x => x.SlaveId);
-      var newClients = slaves
-        .Where(x => !clientIds.Contains(x.ResourceId)
-                    || modifiedClientIds.Contains(x.ResourceId))
-        .Select(x => new {
-          x.ResourceId,
-          x.Name,
-          ResourceGroupId = x.ParentResourceId,
-          GroupName = x.ParentResource != null ? x.ParentResource.Name : null,
-          ResourceGroup2Id = x.ParentResource != null ? x.ParentResource.ParentResourceId : null,
-          GroupName2 = x.ParentResource != null ? x.ParentResource.ParentResource != null ? x.ParentResource.ParentResource.Name : null : null
-        })
-        .ToList();
-
-      var clientsToUpdate = removedClientIds.Union(modifiedClients.Select(x => x.ClientId));
-      dimClientDao.UpdateExpirationTime(clientsToUpdate, DateTime.Now);
-      dimClientDao.Save(newClients.Select(x => new DimClient {
+      var resourceDao = pm.ResourceDao;
+
+      var resources = resourceDao.GetAll().ToList();
+      var dimClients = dimClientDao.GetAllOnlineClients().ToList();
+
+      var onlineClients = dimClients.Where(x => resources.Select(y => y.ResourceId).Contains(x.ResourceId));
+      var addedResources = resources.Where(x => !dimClients.Select(y => y.ResourceId).Contains(x.ResourceId));
+      var removedResources = dimClients.Where(x => !resources.Select(y => y.ResourceId).Contains(x.ResourceId));
+
+      // set expiration time of removed resources
+      foreach(var r in removedResources) {
+        r.DateExpired = DateTime.Now;
+      }
+
+      // add new resources
+      dimClientDao.Save(addedResources.Select(x => new DimClient {
         ResourceId = x.ResourceId,
+        ParentResourceId = x.ParentResourceId,
         Name = x.Name,
-        ExpirationTime = null,
-        ResourceGroupId = x.ResourceGroupId,
-        GroupName = x.GroupName,
-        ResourceGroup2Id = x.ResourceGroup2Id,
-        GroupName2 = x.GroupName2
+        ResourceType = x.ResourceType,
+        DateCreated = DateTime.Now,
+        DateExpired = null
       }));
-    }
+
+      // expire client if its parent has changed and create a new entry
+      // otherwise perform "normal" update
+      foreach(var dimc in onlineClients) {
+        var r = resources.Where(x => x.ResourceId == dimc.ResourceId).SingleOrDefault();
+        if(r != null) {
+          if(dimc.ParentResourceId == null ? r.ParentResourceId != null : dimc.ParentResourceId != r.ParentResourceId) {
+            var now = DateTime.Now;
+            dimc.DateExpired = now;
+            dimClientDao.Save(new DimClient {
+              ResourceId = r.ResourceId,
+              ParentResourceId = r.ParentResourceId,
+              Name = r.Name,
+              ResourceType = r.ResourceType,
+              DateCreated = now,
+              DateExpired = null
+            });
+          } else {
+            dimc.Name = r.Name;
+          }
+        }
+      }
+    }
+
+    //// (1) for new slaves (not yet reported in Table DimClients) ...
+    //// and modified slaves (name or parent resource changed) a new DimClient-entry is created
+    //// (2) for already reported removed and modifid clients the expiration date is set
+    //private void UpdateDimClientsTableOld(PersistenceManager pm) {
+    //  var dimClientDao = pm.DimClientDao;
+    //  var slaveDao = pm.SlaveDao;
+    //  var slaves = slaveDao.GetAll();
+    //  var recentlyAddedClients = dimClientDao.GetAllOnlineClients();
+    //  var slaveIds = slaves.Select(x => x.ResourceId);
+
+    //  var removedClientIds = recentlyAddedClients
+    //    .Where(x => !slaveIds.Contains(x.ResourceId))
+    //    .Select(x => x.Id);
+    //  var modifiedClients =
+    //    from slave in slaves
+    //    join client in recentlyAddedClients on slave.ResourceId equals client.ResourceId
+    //    where (slave.Name != client.Name
+    //           || slave.ParentResourceId == null && client.ResourceGroupId != null // because both can be null and null comparison
+    //           || slave.ParentResourceId != null && client.ResourceGroupId == null // does return no entry on the sql server
+    //           || slave.ParentResourceId != client.ResourceGroupId
+    //           || ((slave.ParentResource != null) && slave.ParentResource.ParentResourceId != client.ResourceGroup2Id))
+    //    select new {
+    //      SlaveId = slave.ResourceId,
+    //      ClientId = client.Id
+    //    };
+    //  var clientIds = dimClientDao.GetAllOnlineClients().Select(x => x.ResourceId);
+    //  var modifiedClientIds = modifiedClients.Select(x => x.SlaveId);
+    //  var newClients = slaves
+    //    .Where(x => !clientIds.Contains(x.ResourceId)
+    //                || modifiedClientIds.Contains(x.ResourceId))
+    //    .Select(x => new {
+    //      x.ResourceId,
+    //      x.Name,
+    //      ResourceGroupId = x.ParentResourceId,
+    //      GroupName = x.ParentResource != null ? x.ParentResource.Name : null,
+    //      ResourceGroup2Id = x.ParentResource != null ? x.ParentResource.ParentResourceId : null,
+    //      GroupName2 = x.ParentResource != null ? x.ParentResource.ParentResource != null ? x.ParentResource.ParentResource.Name : null : null
+    //    })
+    //    .ToList();
+
+    //  var clientsToUpdate = removedClientIds.Union(modifiedClients.Select(x => x.ClientId));
+    //  dimClientDao.UpdateExpirationTime(clientsToUpdate, DateTime.Now);
+    //  dimClientDao.Save(newClients.Select(x => new DimClient {
+    //    ResourceId = x.ResourceId,
+    //    Name = x.Name,
+    //    ExpirationTime = null,
+    //    ResourceGroupId = x.ResourceGroupId,
+    //    GroupName = x.GroupName,
+    //    ResourceGroup2Id = x.ResourceGroup2Id,
+    //    GroupName2 = x.GroupName2
+    //  }));
+    //}
+
 
     private void UpdateFactClientInfoTable(DimTime newTime, PersistenceManager pm) {
@@ -222,5 +368,5 @@
       var newRawFactInfos =
         from s in slaveDao.GetAll()
-        join c in dimClientDao.GetActiveClients() on s.ResourceId equals c.ResourceId
+        join c in dimClientDao.GetAllOnlineSlaves() on s.ResourceId equals c.ResourceId
         join lcf in factClientInfoDao.GetLastUpdateTimestamps() on c.ResourceId equals lcf.ResourceId into joinCf
         from cf in joinCf.DefaultIfEmpty()
@@ -261,5 +407,29 @@
     }
 
-    private void UpdateTaskFactsTable(PersistenceManager pm) {
+    private void UpdateFactProjectInfoTable(DimTime newTime, PersistenceManager pm) {
+      var factProjectInfoDao = pm.FactProjectInfoDao;
+      var dimProjectDao = pm.DimProjectDao;
+      var projectDao = pm.ProjectDao;
+
+      var projectAvailabilityStats = projectDao.GetAvailabilityStatsPerProject();
+      var projectUsageStats = projectDao.GetUsageStatsPerProject();
+      var dimProjects = dimProjectDao.GetAllOnlineProjects().ToList();
+
+      factProjectInfoDao.Save(
+        from dimp in dimProjects
+        let aStats = projectAvailabilityStats.Where(x => x.ProjectId == dimp.ProjectId).SingleOrDefault()
+        let uStats = projectUsageStats.Where(x => x.ProjectId == dimp.ProjectId).SingleOrDefault()
+        select new FactProjectInfo {
+            ProjectId = dimp.Id,
+            DimTime = newTime,
+            NumTotalCores = aStats != null ? aStats.Cores : 0,
+            TotalMemory = aStats != null ? aStats.Memory : 0,
+            NumUsedCores = uStats != null ? uStats.Cores : 0,
+            UsedMemory = uStats != null ? uStats.Memory : 0
+          }
+        );
+    }
+
+    private void UpdateFactTaskTable(PersistenceManager pm) {
       var factTaskDao = pm.FactTaskDao;
       var taskDao = pm.TaskDao;
@@ -267,10 +437,15 @@
 
       var factTaskIds = factTaskDao.GetAll().Select(x => x.TaskId);
-      var notFinishedFactTasks = factTaskDao.GetNotFinishedTasks().Select(x => new {
-        x.TaskId,
-        x.LastClientId
-      });
-
-      var newTasks =
+      var notFinishedFactTasks = factTaskDao.GetNotFinishedTasks();
+      //var notFinishedFactTasks = factTaskDao.GetNotFinishedTasks().Select(x => new {
+      //  x.TaskId,
+      //  x.LastClientId
+      //});
+
+      // query several properties for all new and not finished tasks
+      // in order to use them later either...
+      // (1) to update the fact task entry of not finished tasks
+      // (2) to insert a new fact task entry for new tasks
+      var newAndNotFinishedTasks =
         (from task in taskDao.GetAllChildTasks()
          let stateLogs = task.StateLogs.OrderByDescending(x => x.DateTime)
@@ -280,5 +455,5 @@
          join lastFactTask in notFinishedFactTasks on task.TaskId equals lastFactTask.TaskId into lastFactPerTask
          from lastFact in lastFactPerTask.DefaultIfEmpty()
-         join client in dimClientDao.GetActiveClients() on lastSlaveId equals client.ResourceId into clientsPerSlaveId
+         join client in dimClientDao.GetAllOnlineClients() on lastSlaveId equals client.ResourceId into clientsPerSlaveId
          from client in clientsPerSlaveId.DefaultIfEmpty()
          select new {
@@ -296,7 +471,32 @@
          }).ToList();
 
-      //insert facts for new tasks
+      // (1) update data of already existing facts
+      // i.e. for all in newAndNotFinishedTasks where NotFinishedTask = true
+      foreach (var notFinishedFactTask in notFinishedFactTasks) {
+        var nfftUpdate = newAndNotFinishedTasks.Where(x => x.TaskId == notFinishedFactTask.TaskId).SingleOrDefault();
+        if(nfftUpdate != null) {
+          var taskData = CalculateFactTaskData(nfftUpdate.StateLogs);
+
+          notFinishedFactTask.StartTime = taskData.StartTime;
+          notFinishedFactTask.EndTime = taskData.EndTime;
+          notFinishedFactTask.LastClientId = nfftUpdate.LastClientId;
+          notFinishedFactTask.Priority = nfftUpdate.Priority;
+          notFinishedFactTask.CoresRequired = nfftUpdate.CoresRequired;
+          notFinishedFactTask.MemoryRequired = nfftUpdate.MemoryRequired;
+          notFinishedFactTask.NumCalculationRuns = taskData.CalculationRuns;
+          notFinishedFactTask.NumRetries = taskData.Retries;
+          notFinishedFactTask.WaitingTime = taskData.WaitingTime;
+          notFinishedFactTask.CalculatingTime = taskData.CalculatingTime;
+          notFinishedFactTask.TransferTime = taskData.TransferTime;
+          notFinishedFactTask.TaskState = nfftUpdate.State;
+          notFinishedFactTask.Exception = taskData.Exception;
+          notFinishedFactTask.InitialWaitingTime = taskData.InitialWaitingTime;
+        }
+      }
+
+      // (2) insert facts for new tasks
+      // i.e. for all in newAndNotFinishedTasks where NotFinishedTask = false
       factTaskDao.Save(
-        from x in newTasks
+        from x in newAndNotFinishedTasks
         where !x.NotFinishedTask
         let taskData = CalculateFactTaskData(x.StateLogs)
@@ -320,27 +520,28 @@
         });
 
-      //update data of already existing facts
-      foreach (var notFinishedTask in factTaskDao.GetNotFinishedTasks()) {
-        var ntc = newTasks.Where(x => x.TaskId == notFinishedTask.TaskId);
-        if (ntc.Any()) {
-          var x = ntc.Single();
-          var taskData = CalculateFactTaskData(x.StateLogs);
-
-          notFinishedTask.StartTime = taskData.StartTime;
-          notFinishedTask.EndTime = taskData.EndTime;
-          notFinishedTask.LastClientId = x.LastClientId;
-          notFinishedTask.Priority = x.Priority;
-          notFinishedTask.CoresRequired = x.CoresRequired;
-          notFinishedTask.MemoryRequired = x.MemoryRequired;
-          notFinishedTask.NumCalculationRuns = taskData.CalculationRuns;
-          notFinishedTask.NumRetries = taskData.Retries;
-          notFinishedTask.WaitingTime = taskData.WaitingTime;
-          notFinishedTask.CalculatingTime = taskData.CalculatingTime;
-          notFinishedTask.TransferTime = taskData.TransferTime;
-          notFinishedTask.TaskState = x.State;
-          notFinishedTask.Exception = taskData.Exception;
-          notFinishedTask.InitialWaitingTime = taskData.InitialWaitingTime;
-        }
-      }
+
+      ////update data of already existing facts
+      //foreach (var notFinishedTask in factTaskDao.GetNotFinishedTasks()) {
+      //  var ntc = newTasks.Where(x => x.TaskId == notFinishedTask.TaskId);
+      //  if (ntc.Any()) {
+      //    var x = ntc.Single();
+      //    var taskData = CalculateFactTaskData(x.StateLogs);
+
+      //    notFinishedTask.StartTime = taskData.StartTime;
+      //    notFinishedTask.EndTime = taskData.EndTime;
+      //    notFinishedTask.LastClientId = x.LastClientId;
+      //    notFinishedTask.Priority = x.Priority;
+      //    notFinishedTask.CoresRequired = x.CoresRequired;
+      //    notFinishedTask.MemoryRequired = x.MemoryRequired;
+      //    notFinishedTask.NumCalculationRuns = taskData.CalculationRuns;
+      //    notFinishedTask.NumRetries = taskData.Retries;
+      //    notFinishedTask.WaitingTime = taskData.WaitingTime;
+      //    notFinishedTask.CalculatingTime = taskData.CalculatingTime;
+      //    notFinishedTask.TransferTime = taskData.TransferTime;
+      //    notFinishedTask.TaskState = x.State;
+      //    notFinishedTask.Exception = taskData.Exception;
+      //    notFinishedTask.InitialWaitingTime = taskData.InitialWaitingTime;
+      //  }
+      //}
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Interfaces/IAuthorizationManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Interfaces/IAuthorizationManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Interfaces/IAuthorizationManager.cs	(revision 16240)
@@ -22,4 +22,5 @@
 using System;
 using HeuristicLab.Services.Hive.DataTransfer;
+using System.Collections.Generic;
 
 namespace HeuristicLab.Services.Hive {
@@ -35,4 +36,12 @@
 
     void AuthorizeForResourceAdministration(Guid resourceId);
+
+    void AuthorizeForProjectAdministration(Guid projectId, bool parentalOwnership);
+
+    void AuthorizeForProjectResourceAdministration(Guid projectId, IEnumerable<Guid> resourceIds);
+
+    void AuthorizeProjectForResourcesUse(Guid projectId, IEnumerable<Guid> resourceIds);
+
+    void AuthorizeUserForProjectUse(Guid userId, Guid projectId);
   }
 }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/AuthorizationManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/AuthorizationManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/AuthorizationManager.cs	(revision 16240)
@@ -27,10 +27,19 @@
 using DA = HeuristicLab.Services.Hive.DataAccess;
 using DT = HeuristicLab.Services.Hive.DataTransfer;
-
+using System.Collections.Generic;
+using System.Linq;
 
 namespace HeuristicLab.Services.Hive {
   public class AuthorizationManager : IAuthorizationManager {
 
-    private const string NOT_AUTHORIZED = "Current user is not authorized to access the requested resource";
+    private const string NOT_AUTHORIZED_USERRESOURCE = "Current user is not authorized to access the requested resource";
+    private const string NOT_AUTHORIZED_USERPROJECT = "Current user is not authorized to access the requested project";
+    private const string NOT_AUTHORIZED_USERJOB = "Current user is not authorized to access the requested job";
+    private const string NOT_AUTHORIZED_PROJECTRESOURCE = "Selected project is not authorized to access the requested resource";
+    private const string USER_NOT_IDENTIFIED = "User could not be identified";
+    private const string JOB_NOT_EXISTENT = "Queried job could not be found";
+    private const string TASK_NOT_EXISTENT = "Queried task could not be found";
+    private const string PROJECT_NOT_EXISTENT = "Queried project could not be found";
+
     private IPersistenceManager PersistenceManager {
       get { return ServiceLocator.Instance.PersistenceManager; }
@@ -47,14 +56,24 @@
     public void Authorize(Guid userId) {
       if (userId != ServiceLocator.Instance.UserManager.CurrentUserId)
-        throw new SecurityException(NOT_AUTHORIZED);
+        throw new SecurityException(NOT_AUTHORIZED_USERRESOURCE);
     }
 
     public void AuthorizeForTask(Guid taskId, DT.Permission requiredPermission) {
       if (ServiceLocator.Instance.RoleVerifier.IsInRole(HiveRoles.Slave)) return; // slave-users can access all tasks
+      if (ServiceLocator.Instance.RoleVerifier.IsInRole(HiveRoles.Administrator)) return; // administrator can access all tasks
+      var currentUserId = UserManager.CurrentUserId;
       var pm = PersistenceManager;
       var taskDao = pm.TaskDao;
+      var projectDao = pm.ProjectDao;
       pm.UseTransaction(() => {
         var task = taskDao.GetById(taskId);
-        if (task == null) throw new SecurityException(NOT_AUTHORIZED);
+        if (task == null) throw new SecurityException(TASK_NOT_EXISTENT);
+
+        // check if user is granted to administer a job-parenting project
+        var administrationGrantedProjects = projectDao
+          .GetAdministrationGrantedProjectsForUser(currentUserId)
+          .ToList();
+        if (administrationGrantedProjects.Contains(task.Job.Project)) return;
+
         AuthorizeJob(pm, task.JobId, requiredPermission);
       });
@@ -62,21 +81,125 @@
 
     public void AuthorizeForJob(Guid jobId, DT.Permission requiredPermission) {
-      var pm = PersistenceManager;
-      pm.UseTransaction(() => {
+      if (ServiceLocator.Instance.RoleVerifier.IsInRole(HiveRoles.Administrator)) return; // administrator can access all jobs
+      var currentUserId = UserManager.CurrentUserId;
+      var pm = PersistenceManager;
+      var jobDao = pm.JobDao;
+      var projectDao = pm.ProjectDao;
+      pm.UseTransaction(() => {
+        var job = jobDao.GetById(jobId);
+        if(job == null) throw new SecurityException(JOB_NOT_EXISTENT);
+
+        // check if user is granted to administer a job-parenting project
+        var administrationGrantedProjects = projectDao
+          .GetAdministrationGrantedProjectsForUser(currentUserId)
+          .ToList();
+        if (administrationGrantedProjects.Contains(job.Project)) return;
+
         AuthorizeJob(pm, jobId, requiredPermission);
       });
     }
 
+    // authorize if user is admin or resource owner
     public void AuthorizeForResourceAdministration(Guid resourceId) {
+      var currentUserId = UserManager.CurrentUserId;
       var pm = PersistenceManager;
       var resourceDao = pm.ResourceDao;
       pm.UseTransaction(() => {
         var resource = resourceDao.GetById(resourceId);
-        if (resource == null) throw new SecurityException(NOT_AUTHORIZED);
-        if (resource.OwnerUserId != UserManager.CurrentUserId
+        if (resource == null) throw new SecurityException(NOT_AUTHORIZED_USERRESOURCE);
+
+        if (resource.OwnerUserId != currentUserId
             && !RoleVerifier.IsInRole(HiveRoles.Administrator)) {
-          throw new SecurityException(NOT_AUTHORIZED);
-        }
-      });
+          throw new SecurityException(NOT_AUTHORIZED_USERRESOURCE);
+        }
+      });
+    }
+
+    // authorize if user is admin, project owner or owner of a parent project
+    public void AuthorizeForProjectAdministration(Guid projectId, bool parentalOwnership) {
+      if (projectId == null || projectId == Guid.Empty) return;
+      var currentUserId = UserManager.CurrentUserId;
+      var pm = PersistenceManager;
+      var projectDao = pm.ProjectDao;
+      pm.UseTransaction(() => {
+        var project = projectDao.GetById(projectId);
+        if (project == null) throw new ArgumentException(PROJECT_NOT_EXISTENT);
+        if(!RoleVerifier.IsInRole(HiveRoles.Administrator)
+          && !project.ParentProjectId.HasValue) {
+          throw new SecurityException(NOT_AUTHORIZED_USERPROJECT);
+        }
+
+        List<Project> projectBranch = null;
+        if(parentalOwnership) projectBranch = projectDao.GetParentProjectsById(projectId).ToList();
+        else projectBranch = projectDao.GetCurrentAndParentProjectsById(projectId).ToList();
+
+        if(!RoleVerifier.IsInRole(HiveRoles.Administrator)
+            && !projectBranch.Select(x => x.OwnerUserId).Contains(currentUserId)) {
+          throw new SecurityException(NOT_AUTHORIZED_USERPROJECT);
+        }
+      });
+    }
+
+    // authorize if user is admin, or owner of a project or parent project, for which the resources are assigned to
+    public void AuthorizeForProjectResourceAdministration(Guid projectId, IEnumerable<Guid> resourceIds) {
+      if (projectId == null || projectId == Guid.Empty) return;
+      var currentUserId = UserManager.CurrentUserId;
+      var pm = PersistenceManager;
+      var projectDao = pm.ProjectDao;
+      var resourceDao = pm.ResourceDao;
+      var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+      pm.UseTransaction(() => {
+        // check if project exists (not necessary)
+        var project = projectDao.GetById(projectId);
+        if (project == null) throw new SecurityException(NOT_AUTHORIZED_USERRESOURCE);
+
+        // check if resourceIds exist
+        if (resourceIds != null && resourceIds.Any() && !resourceDao.CheckExistence(resourceIds))
+          throw new SecurityException(NOT_AUTHORIZED_USERRESOURCE);
+
+        // check if user is admin
+        if (RoleVerifier.IsInRole(HiveRoles.Administrator)) return;
+
+        // check if user is owner of the project or a parent project
+        var projectBranch = projectDao.GetCurrentAndParentProjectsById(projectId).ToList();
+        if (!projectBranch.Select(x => x.OwnerUserId).Contains(currentUserId)
+            && !RoleVerifier.IsInRole(HiveRoles.Administrator)) {
+          throw new SecurityException(NOT_AUTHORIZED_USERPROJECT);
+        }
+
+        // check if the all argument resourceIds are among the assigned resources of the owned projects
+        var grantedResourceIds = assignedProjectResourceDao.GetAllGrantedResourceIdsOfOwnedParentProjects(projectId, currentUserId).ToList();
+        if(resourceIds.Except(grantedResourceIds).Any()) {
+          throw new SecurityException(NOT_AUTHORIZED_USERRESOURCE);
+        }
+      });
+    }
+
+    // Check if a project is authorized to use a list of resources
+    public void AuthorizeProjectForResourcesUse(Guid projectId, IEnumerable<Guid> resourceIds) {
+      if (projectId == null || projectId == Guid.Empty || resourceIds == null || !resourceIds.Any()) return;
+      var pm = PersistenceManager;
+      var assignedProjectResourceDao = pm.AssignedProjectResourceDao;
+      if (!assignedProjectResourceDao.CheckProjectGrantedForResources(projectId, resourceIds))
+        throw new SecurityException(NOT_AUTHORIZED_PROJECTRESOURCE);
+    }
+
+    // Check if current user is authorized to use an explicit project (e.g. in order to add a job)
+    // note: administrators and project owner are NOT automatically granted
+    public void AuthorizeUserForProjectUse(Guid userId, Guid projectId) {
+      if(userId == null || userId == Guid.Empty) {
+        throw new SecurityException(USER_NOT_IDENTIFIED);
+      }
+      if(projectId == null) return;
+
+      var pm = PersistenceManager;
+      // collect current and group membership Ids
+      var userAndGroupIds = new List<Guid>() { userId };
+      userAndGroupIds.AddRange(UserManager.GetUserGroupIdsOfUser(userId));
+      // perform the actual check
+      var projectPermissionDao = pm.ProjectPermissionDao;
+      if (!projectPermissionDao.CheckUserGrantedForProject(projectId, userAndGroupIds)) {
+        throw new SecurityException(NOT_AUTHORIZED_USERPROJECT);
+      }
     }
 
@@ -93,9 +216,10 @@
 
     private void AuthorizeJob(IPersistenceManager pm, Guid jobId, DT.Permission requiredPermission) {
+      var currentUserId = UserManager.CurrentUserId;
       var requiredPermissionEntity = requiredPermission.ToEntity();
-      DA.Permission permission = GetPermissionForJob(pm, jobId, UserManager.CurrentUserId);
+      DA.Permission permission = GetPermissionForJob(pm, jobId, currentUserId);
       if (permission == Permission.NotAllowed
           || ((permission != requiredPermissionEntity) && requiredPermissionEntity == Permission.Full)) {
-        throw new SecurityException(NOT_AUTHORIZED);
+        throw new SecurityException(NOT_AUTHORIZED_USERJOB);
       }
     }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/EventManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/EventManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/EventManager.cs	(revision 16240)
@@ -34,4 +34,10 @@
     public void Cleanup() {
       var pm = PersistenceManager;
+
+      pm.UseTransaction(() => {
+        FinishJobDeletion(pm);
+        pm.SubmitChanges();
+      });
+
       pm.UseTransaction(() => {
         SetTimeoutSlavesOffline(pm);
@@ -45,4 +51,13 @@
         pm.SubmitChanges();
       });
+    }
+
+    /// <summary>
+    /// Deletes all jobs which are in state "DeletionPending" (this will include all corresponding tasks).
+    /// The state "DeletionPending" is set by HiveJanitor > StatisticsGenerator
+    /// </summary>
+    private void FinishJobDeletion(IPersistenceManager pm) {
+      var jobDao = pm.JobDao;
+      jobDao.DeleteByState(JobState.DeletionPending);
     }
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/HeartbeatManager.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/HeartbeatManager.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/Manager/HeartbeatManager.cs	(revision 16240)
@@ -142,61 +142,73 @@
     private IEnumerable<MessageContainer> UpdateTasks(IPersistenceManager pm, Heartbeat heartbeat, bool isAllowedToCalculate) {
       var taskDao = pm.TaskDao;
-      var assignedResourceDao = pm.AssignedResourceDao;
+      var jobDao = pm.JobDao;
+      var assignedJobResourceDao = pm.AssignedJobResourceDao;
       var actions = new List<MessageContainer>();
       if (heartbeat.JobProgress == null || !heartbeat.JobProgress.Any())
         return actions;
 
-      if (!isAllowedToCalculate && heartbeat.JobProgress.Count != 0) {
-        actions.Add(new MessageContainer(MessageContainer.MessageType.PauseAll));
-      } else {
-        // select all tasks and statelogs with one query
-        var taskIds = heartbeat.JobProgress.Select(x => x.Key).ToList();
-        var taskInfos = pm.UseTransaction(() =>
-          (from task in taskDao.GetAll()
-           where taskIds.Contains(task.TaskId)
-           let lastStateLog = task.StateLogs.OrderByDescending(x => x.DateTime).FirstOrDefault()
-           select new {
-             TaskId = task.TaskId,
-             Command = task.Command,
-             SlaveId = lastStateLog != null ? lastStateLog.SlaveId : default(Guid)
-           }).ToList()
-        );
-
-        // process the jobProgresses
-        foreach (var jobProgress in heartbeat.JobProgress) {
-          var progress = jobProgress;
-          var curTask = taskInfos.SingleOrDefault(x => x.TaskId == progress.Key);
-          if (curTask == null) {
-            actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, progress.Key));
-            LogFactory.GetLogger(this.GetType().Namespace).Log("Task on slave " + heartbeat.SlaveId + " does not exist in DB: " + jobProgress.Key);
-          } else {
-            var slaveId = curTask.SlaveId;
-            if (slaveId == Guid.Empty || slaveId != heartbeat.SlaveId) {
-              // assigned slave does not match heartbeat
+      var jobIdsWithStatisticsPending = jobDao.GetJobIdsByState(DA.JobState.StatisticsPending).ToList();
+
+      // select all tasks and statelogs with one query
+      var taskIds = heartbeat.JobProgress.Select(x => x.Key).ToList();
+      var taskInfos = pm.UseTransaction(() =>
+        (from task in taskDao.GetAll()
+          where taskIds.Contains(task.TaskId)
+          let lastStateLog = task.StateLogs.OrderByDescending(x => x.DateTime).FirstOrDefault()
+          select new {
+            TaskId = task.TaskId,
+            JobId = task.JobId,
+            State = task.State,
+            Command = task.Command,
+            SlaveId = lastStateLog != null ? lastStateLog.SlaveId : default(Guid)
+          }).ToList()
+      );
+
+      // process the jobProgresses
+      foreach (var jobProgress in heartbeat.JobProgress) {
+        var progress = jobProgress;
+        var curTask = taskInfos.SingleOrDefault(x => x.TaskId == progress.Key);
+        if (curTask == null) {
+          actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, progress.Key));
+          LogFactory.GetLogger(this.GetType().Namespace).Log("Task on slave " + heartbeat.SlaveId + " does not exist in DB: " + jobProgress.Key);
+        } else if (jobIdsWithStatisticsPending.Contains(curTask.JobId)) {
+          // parenting job of current task has been requested for deletion (indicated by job state "Statistics Pending")
+          // update task execution time
+          pm.UseTransaction(() => {
+            taskDao.UpdateExecutionTime(curTask.TaskId, progress.Value.TotalMilliseconds);
+          });
+          actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, curTask.TaskId));
+          LogFactory.GetLogger(this.GetType().Namespace).Log("Abort task " + curTask.TaskId + " on slave " + heartbeat.SlaveId + ". The parenting job " + curTask.JobId + " was requested to be deleted.");
+        } else if (curTask.SlaveId == Guid.Empty || curTask.SlaveId != heartbeat.SlaveId) {
+          // assigned slave does not match heartbeat
+          actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, curTask.TaskId));
+          LogFactory.GetLogger(this.GetType().Namespace).Log("The slave " + heartbeat.SlaveId + " is not supposed to calculate task: " + curTask.TaskId);
+        } else if (!isAllowedToCalculate) {
+          actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
+          LogFactory.GetLogger(this.GetType().Namespace).Log("The slave " + heartbeat.SlaveId + " is not allowed to calculate any tasks tue to a downtime. The task is paused.");
+        } else if (!assignedJobResourceDao.CheckJobGrantedForResource(curTask.JobId, heartbeat.SlaveId)) {
+          // slaveId (and parent resourceGroupIds) are not among the assigned resources ids for task-parenting job
+          // this might happen when (a) job-resource assignment has been changed (b) slave is moved to different group
+          actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
+          LogFactory.GetLogger(this.GetType().Namespace).Log("The slave " + heartbeat.SlaveId + " is not granted to calculate task: " + curTask.TaskId + " of job: " + curTask.JobId);
+        } else {
+          // update task execution time
+          pm.UseTransaction(() => {
+            taskDao.UpdateExecutionTime(curTask.TaskId, progress.Value.TotalMilliseconds);
+          });
+          switch (curTask.Command) {
+            case DA.Command.Stop:
+              actions.Add(new MessageContainer(MessageContainer.MessageType.StopTask, curTask.TaskId));
+              break;
+            case DA.Command.Pause:
+              actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
+              break;
+            case DA.Command.Abort:
               actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, curTask.TaskId));
-              LogFactory.GetLogger(this.GetType().Namespace).Log("The slave " + heartbeat.SlaveId + " is not supposed to calculate task: " + curTask.TaskId);
-            } else if (!assignedResourceDao.TaskIsAllowedToBeCalculatedBySlave(curTask.TaskId, heartbeat.SlaveId)) {
-              // assigned resources ids of task do not match with slaveId (and parent resourceGroupIds); this might happen when slave is moved to different group
-              actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
-            } else {
-              // update task execution time
-              pm.UseTransaction(() => {
-                taskDao.UpdateExecutionTime(curTask.TaskId, progress.Value.TotalMilliseconds);
-              });
-              switch (curTask.Command) {
-                case DA.Command.Stop:
-                  actions.Add(new MessageContainer(MessageContainer.MessageType.StopTask, curTask.TaskId));
-                  break;
-                case DA.Command.Pause:
-                  actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
-                  break;
-                case DA.Command.Abort:
-                  actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, curTask.TaskId));
-                  break;
-              }
-            }
-          }
-        }
-      }
+              break;
+          }
+        }
+        
+      } 
       return actions;
     }
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/ServiceContracts/IHiveService.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/ServiceContracts/IHiveService.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.Hive/3.3/ServiceContracts/IHiveService.cs	(revision 16240)
@@ -31,6 +31,7 @@
   public interface IHiveService {
     #region Task Methods
-    [OperationContract]
-    Guid AddTask(Task task, TaskData taskData, IEnumerable<Guid> resourceIds);
+
+    [OperationContract]
+    Guid AddTask(Task task, TaskData taskData);
 
     [OperationContract]
@@ -78,11 +79,23 @@
 
     [OperationContract]
-    Guid AddJob(Job jobDto);
-
-    [OperationContract]
-    void UpdateJob(Job jobDto);
-
-    [OperationContract]
-    void DeleteJob(Guid JobId);
+    IEnumerable<Job> GetJobsByProjectId(Guid projectId);
+
+    [OperationContract]
+    IEnumerable<Job> GetJobsByProjectIds(IEnumerable<Guid> projectIds);
+
+    [OperationContract]
+    Guid AddJob(Job jobDto, IEnumerable<Guid> resourceIds);
+
+    [OperationContract]
+    void UpdateJob(Job jobDto, IEnumerable<Guid> resourceIds);
+
+    [OperationContract]
+    void UpdateJobState(Guid JobId, JobState jobState);
+
+    [OperationContract]
+    void UpdateJobStates(IEnumerable<Guid> jobIds, JobState jobState);
+
+    [OperationContract]
+    IEnumerable<AssignedJobResource> GetAssignedResourcesForJob(Guid jobId);
     #endregion
 
@@ -132,13 +145,62 @@
     #endregion
 
-    #region ResourcePermission Methods
-    [OperationContract]
-    void GrantResourcePermissions(Guid resourceId, Guid[] grantedUserIds);
-
-    [OperationContract]
-    void RevokeResourcePermissions(Guid resourceId, Guid[] grantedUserIds);
-
-    [OperationContract]
-    IEnumerable<ResourcePermission> GetResourcePermissions(Guid resourceId);
+    #region Project Methods
+    [OperationContract]
+    Guid AddProject(Project projectDto);
+
+    [OperationContract]
+    void UpdateProject(Project projectDto);
+
+    [OperationContract]
+    void DeleteProject(Guid projectId);
+
+    [OperationContract]
+    Project GetProject(Guid projectId);
+
+    [OperationContract]
+    IEnumerable<Project> GetProjects();
+
+    [OperationContract]
+    IEnumerable<Project> GetProjectsForAdministration();
+
+    [OperationContract]
+    IDictionary<Guid, HashSet<Guid>> GetProjectGenealogy();
+
+    [OperationContract]
+    IDictionary<Guid, string> GetProjectNames();
+    #endregion
+
+    #region ProjectPermission Methods
+    [OperationContract]
+    void SaveProjectPermissions(Guid projectId, List<Guid> grantedUserIds, bool reassign, bool cascading, bool reassignCascading);
+
+    //[OperationContract]
+    //void GrantProjectPermissions(Guid projectId, List<Guid> grantedUserIds, bool cascading);
+
+    //[OperationContract]
+    //void RevokeProjectPermissions(Guid projectId, List<Guid> grantedUserIds, bool cascading);
+
+    [OperationContract]
+    IEnumerable<ProjectPermission> GetProjectPermissions(Guid projectId);
+    #endregion
+
+    #region AssignedProjectResource Methods
+    [OperationContract]
+    void SaveProjectResourceAssignments(Guid projectId, List<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading);
+
+    //[OperationContract]
+    //void AssignProjectResources(Guid projectId, List<Guid> resourceIds, bool cascading);
+
+    //[OperationContract]
+    //void UnassignProjectResources(Guid projectId, List<Guid> resourceIds, bool cascading);
+
+    [OperationContract]
+    IEnumerable<AssignedProjectResource> GetAssignedResourcesForProject(Guid projectId);
+
+    [OperationContract]
+    IEnumerable<AssignedProjectResource> GetAssignedResourcesForProjectAdministration(Guid projectId);
+
+    [OperationContract]
+    IEnumerable<AssignedProjectResource> GetAssignedResourcesForProjectsAdministration(IEnumerable<Guid> projectIds);
     #endregion
 
@@ -160,4 +222,16 @@
 
     [OperationContract]
+    IEnumerable<Slave> GetSlavesForAdministration();
+
+    [OperationContract]
+    IEnumerable<SlaveGroup> GetSlaveGroupsForAdministration();
+
+    [OperationContract]
+    IDictionary<Guid, HashSet<Guid>> GetResourceGenealogy();
+
+    [OperationContract]
+    IDictionary<Guid, string> GetResourceNames();
+
+    [OperationContract]
     void UpdateSlave(Slave slave);
 
@@ -207,4 +281,10 @@
     [OperationContract]
     Guid GetUserIdByUsername(string username);
+
+    [OperationContract]
+    Dictionary<Guid, HashSet<Guid>> GetUserGroupTree();
+
+    [OperationContract]
+    bool CheckAccessToAdminAreaGranted();
     #endregion
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/OKB.dbml
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/OKB.dbml	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/OKB.dbml	(revision 16240)
@@ -1,4 +1,4 @@
 ﻿<?xml version="1.0" encoding="utf-8"?><Database Name="HeuristicLab.OKB" EntityNamespace="HeuristicLab.Services.OKB.DataAccess" ContextNamespace="HeuristicLab.Services.OKB.DataAccess" Class="OKBDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
-  <Connection Mode="AppSettings" ConnectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB" SettingsObjectName="HeuristicLab.Services.OKB.DataAccess.Properties.Settings" SettingsPropertyName="OKBConnectionString" Provider="System.Data.SqlClient" />
+  <Connection Mode="AppSettings" ConnectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB" SettingsObjectName="HeuristicLab.Services.OKB.DataAccess.Properties.Settings" SettingsPropertyName="OKBConnectionString" Provider="System.Data.SqlClient" />
   <Table Name="dbo.AlgorithmClass" Member="AlgorithmClasses">
     <Type Name="AlgorithmClass">
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/Properties/Settings.Designer.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 16240)
@@ -2,5 +2,5 @@
 // <auto-generated>
 //     This code was generated by a tool.
-//     Runtime Version:4.0.30319.34014
+//     Runtime Version:4.0.30319.42000
 //
 //     Changes to this file may cause incorrect behavior and will be lost if
@@ -13,5 +13,5 @@
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
     public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
         
@@ -27,6 +27,5 @@
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
         [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
-        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=.\\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OK" +
-            "B")]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB")]
         public string OKBConnectionString {
             get {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/Properties/Settings.settings
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/Properties/Settings.settings	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/Properties/Settings.settings	(revision 16240)
@@ -6,8 +6,7 @@
       <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
 &lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
-  &lt;ConnectionString&gt;Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB&lt;/ConnectionString&gt;
-  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
+  &lt;ConnectionString&gt;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB&lt;/ConnectionString&gt;
 &lt;/SerializableConnectionString&gt;</DesignTimeValue>
-      <Value Profile="(Default)">Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB</Value>
+      <Value Profile="(Default)">Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB</Value>
     </Setting>
   </Settings>
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB.DataAccess/3.3/app.config	(revision 16240)
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
+  <configSections>
+  </configSections>
   <connectionStrings>
-    <add name="HeuristicLab.Services.OKB.DataAccess.Properties.Settings.OKBConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB" />
+    <add name="HeuristicLab.Services.OKB.DataAccess.Properties.Settings.OKBConnectionString"
+      connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB" />
   </connectionStrings>
 
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB/3.3/Web.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB/3.3/Web.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB/3.3/Web.config	(revision 16240)
@@ -2,6 +2,6 @@
 <configuration>
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication"/>
-    <add name="HeuristicLab.Services.OKB.DataAccess.Properties.Settings.OKBConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB"/>
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication"/>
+    <add name="HeuristicLab.Services.OKB.DataAccess.Properties.Settings.OKBConnectionString" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB"/>
   </connectionStrings>
 
@@ -34,5 +34,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/OKB-3.3/AdministrationService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/OKB-3.3/AdministrationService.svc" />
           </baseAddresses>
         </host>
@@ -41,10 +41,10 @@
         <endpoint binding="wsHttpBinding" bindingConfiguration="DefaultWsHttpBinding"
           name="WSHttpBinding_IQueryService" contract="HeuristicLab.Services.OKB.Query.IQueryService" />
-        <endpoint address="net.tcp://localhost:8001/OKB-3.3/QueryService.svc"
+        <endpoint address="net.tcp://services.heuristiclab.com/OKB-3.3/QueryService.svc"
           binding="netTcpBinding" bindingConfiguration="DefaultNetBinding"
           name="NetBinding_IQueryService" contract="HeuristicLab.Services.OKB.Query.IQueryService" />
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/OKB-3.3/QueryService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/OKB-3.3/QueryService.svc" />
           </baseAddresses>
         </host>
@@ -55,5 +55,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/OKB-3.3/RunCreationService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/OKB-3.3/RunCreationService.svc" />
           </baseAddresses>
         </host>
@@ -91,5 +91,5 @@
             roleProviderName="AspNetSqlRoleProvider" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB/3.3/app.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB/3.3/app.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.OKB/3.3/app.config	(revision 16240)
@@ -2,6 +2,6 @@
 <configuration>
   <connectionStrings>
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication"/>
-    <add name="HeuristicLab.Services.OKB.DataAccess.Properties.Settings.OKBConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB"/>
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication"/>
+    <add name="HeuristicLab.Services.OKB.DataAccess.Properties.Settings.OKBConnectionString" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.OKB"/>
   </connectionStrings>
 
@@ -34,5 +34,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/OKB-3.3/AdministrationService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/OKB-3.3/AdministrationService.svc" />
           </baseAddresses>
         </host>
@@ -41,10 +41,10 @@
         <endpoint binding="wsHttpBinding" bindingConfiguration="DefaultWsHttpBinding"
           name="WSHttpBinding_IQueryService" contract="HeuristicLab.Services.OKB.Query.IQueryService" />
-        <endpoint address="net.tcp://localhost:8001/OKB-3.3/QueryService.svc"
+        <endpoint address="net.tcp://services.heuristiclab.com/OKB-3.3/QueryService.svc"
           binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IQueryService"
           name="NetBinding_IQueryService" contract="HeuristicLab.Services.OKB.Query.IQueryService" />
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/OKB-3.3/QueryService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/OKB-3.3/QueryService.svc" />
           </baseAddresses>
         </host>
@@ -55,5 +55,5 @@
         <host>
           <baseAddresses>
-            <add baseAddress="http://localhost:8000/OKB-3.3/RunCreationService.svc" />
+            <add baseAddress="http://services.heuristiclab.com/OKB-3.3/RunCreationService.svc" />
           </baseAddresses>
         </host>
@@ -91,5 +91,5 @@
             roleProviderName="AspNetSqlRoleProvider" />
           <serviceCredentials>
-            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
+            <serviceCertificate findValue="services.heuristiclab.com" x509FindType="FindBySubjectName" />
             <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
               membershipProviderName="AspNetSqlMembershipProvider" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Maintenance/3.3/WebApi/SpaceUsageController.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Maintenance/3.3/WebApi/SpaceUsageController.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Maintenance/3.3/WebApi/SpaceUsageController.cs	(revision 16240)
@@ -36,5 +36,6 @@
     public IEnumerable<TableInformation> GetHiveTableInformation() {
       var tables = new List<string> {
-        "AssignedResources",
+        "AssignedJobResource",
+        "AssignedProjectResource",
         "Downtime",
         "Job",
@@ -43,7 +44,8 @@
         "Plugin",
         "PluginData",
+        "Project",
+        "ProjectPermission",
         "RequiredPlugins",
         "Resource",
-        "ResourcePermission",
         "StateLog",
         "Task",
@@ -65,7 +67,9 @@
         "statistics.DimClient",
         "statistics.DimJob",
+        "statistics.DimProject",
         "statistics.DimTime",
         "statistics.DimUser",
         "statistics.FactClientInfo",
+        "statistics.FactProjectInfo",
         "statistics.FactTask"
       };
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Statistics/3.3/WebApi/ClientController.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Statistics/3.3/WebApi/ClientController.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Statistics/3.3/WebApi/ClientController.cs	(revision 16240)
@@ -74,5 +74,6 @@
                   on client.Id equals info.ClientId into clientInfoJoin
                 from clientInfo in clientInfoJoin.OrderByDescending(x => x.Time).Take(1)
-                let offline = (client.ExpirationTime != null || clientInfo.SlaveState == SlaveState.Offline)
+                let offline = (client.DateExpired != null || clientInfo.SlaveState == SlaveState.Offline)
+                let parent = client.ParentResourceId.HasValue ? dimClientDao.GetById(client.ParentResourceId.Value) : null
                 select new DT.ClientDetails {
                   Id = client.Id,
@@ -85,6 +86,6 @@
                   State = offline ? SlaveState.Offline.ToString() : clientInfo.SlaveState.ToString(),
                   LastUpdate = clientInfo.Time,
-                  GroupId = client.ResourceGroupId,
-                  GroupName = client.GroupName,
+                  GroupId = client.ParentResourceId,
+                  GroupName = parent != null ? parent.Name : null,
                   UpTime = offline ? 0 : upTime,
                   TotalUnavailableTime = timeData != null ? timeData.TotalUnavailableTime : 0,
@@ -117,5 +118,5 @@
       var factClientInfoDao = pm.FactClientInfoDao;
       return pm.UseTransaction(() => {
-        var clients = expired ? dimClientDao.GetExpiredClients() : dimClientDao.GetActiveClients();
+        var clients = expired ? dimClientDao.GetAllExpiredSlaves() : dimClientDao.GetAllOnlineSlaves();
         var query = (from client in clients
                      join info in factClientInfoDao.GetAll()
@@ -123,4 +124,5 @@
                      from clientInfo in clientInfoJoin.OrderByDescending(x => x.Time).Take(1)
                      let offline = (expired || clientInfo.SlaveState == SlaveState.Offline)
+                     let parent = client.ParentResourceId.HasValue ? dimClientDao.GetById(client.ParentResourceId.Value) : null
                      select new DT.Client {
                        Id = client.Id,
@@ -132,6 +134,6 @@
                        CpuUtilization = offline ? 0 : clientInfo.CpuUtilization,
                        State = offline ? SlaveState.Offline.ToString() : clientInfo.SlaveState.ToString(),
-                       GroupId = client.ResourceGroupId,
-                       GroupName = client.GroupName,
+                       GroupId = client.ParentResourceId,
+                       GroupName = parent != null ? parent.Name : null,
                        IsAllowedToCalculate = clientInfo.IsAllowedToCalculate
                      });
@@ -200,6 +202,6 @@
       var factClientInfoDao = pm.FactClientInfoDao;
       return pm.UseTransaction(() => {
-        var clients = expired ? dimClientDao.GetExpiredClients() : dimClientDao.GetActiveClients();
-        clients = clients.Where(x => x.ResourceGroupId == id);
+        var clients = expired ? dimClientDao.GetAllExpiredClients() : dimClientDao.GetAllOnlineClients();
+        clients = clients.Where(x => x.ParentResourceId == id);
         var query = (from client in clients
                      join info in factClientInfoDao.GetAll()
@@ -207,4 +209,5 @@
                      from clientInfo in clientInfoJoin.OrderByDescending(x => x.Time).Take(1)
                      let offline = (expired || clientInfo.SlaveState == SlaveState.Offline)
+                     let parent = client.ParentResourceId.HasValue ? dimClientDao.GetById(client.ParentResourceId.Value) : null
                      select new DT.Client {
                        Id = client.Id,
@@ -216,6 +219,6 @@
                        CpuUtilization = offline ? 0 : clientInfo.CpuUtilization,
                        State = offline ? SlaveState.Offline.ToString() : clientInfo.SlaveState.ToString(),
-                       GroupId = client.ResourceGroupId,
-                       GroupName = client.GroupName,
+                       GroupId = client.ParentResourceId,
+                       GroupName = parent != null ? parent.Name : null,
                        IsAllowedToCalculate = clientInfo.IsAllowedToCalculate
                      });
@@ -247,5 +250,5 @@
           client => client.Id,
           (fact, client) => new {
-            client.ResourceGroupId,
+            client.ParentResourceId,
             fact.Time,
             fact.CpuUtilization,
@@ -255,5 +258,5 @@
             fact.UsedMemory
           })
-        .Where(x => x.ResourceGroupId == id)
+        .Where(x => x.ParentResourceId == id)
         .ToList();
       var clientStatus = new DT.ClientStatus {
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Statistics/3.3/WebApi/GroupController.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Statistics/3.3/WebApi/GroupController.cs	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp.Statistics/3.3/WebApi/GroupController.cs	(revision 16240)
@@ -42,11 +42,11 @@
         var clientTimeData = factClientInfoDao.GetAll()
           .Join(dimClientDao.GetAll(), x => x.ClientId, y => y.Id, (x, y) => new {
-            y.ResourceGroupId,
+            y.ParentResourceId,
             x.IdleTime,
             x.OfflineTime,
             x.UnavailableTime
           })
-          .Where(x => x.ResourceGroupId == id)
-          .GroupBy(x => x.ResourceGroupId)
+          .Where(x => x.ParentResourceId == id)
+          .GroupBy(x => x.ParentResourceId)
           .Select(x => new {
             TotalIdleTime = x.Sum(y => y.IdleTime),
@@ -68,12 +68,13 @@
           })
           .FirstOrDefault();
-        return (from client in dimClientDao.GetActiveClients().Where(x => x.ResourceGroupId == id)
+        return (from client in dimClientDao.GetAllOnlineSlaves().Where(x => x.ParentResourceId == id)
                 join info in factClientInfoDao.GetAll()
                   on client.Id equals info.ClientId into clientInfoJoin
                 from clientInfo in clientInfoJoin.OrderByDescending(x => x.Time).Take(1)
                 let offline = (clientInfo.SlaveState == DA.SlaveState.Offline)
+                let parent = client.ParentResourceId.HasValue ? dimClientDao.GetById(client.ParentResourceId.Value) : null
                 select new {
-                  ResourceGroupId = client.ResourceGroupId,
-                  GroupName = client.GroupName,
+                  ResourceGroupId = client.ParentResourceId,
+                  GroupName = parent != null ? parent.Name : null,
                   TotalCores = clientInfo.NumTotalCores,
                   UsedCores = offline ? 0 : clientInfo.NumUsedCores,
@@ -114,5 +115,5 @@
       var dimClientDao = pm.DimClientDao;
       var factClientInfoDao = pm.FactClientInfoDao;
-      var data = (from client in dimClientDao.GetActiveClients()
+      var data = (from client in dimClientDao.GetAllOnlineSlaves()
                   join info in factClientInfoDao.GetAll()
                     on client.Id equals info.ClientId into clientInfoJoin
@@ -120,5 +121,5 @@
                   let offline = (clientInfo.SlaveState == DA.SlaveState.Offline)
                   select new {
-                    ResourceGroupId = client.ResourceGroupId,
+                    ResourceGroupId = client.ParentResourceId,
                     TotalCores = clientInfo.NumTotalCores,
                     UsedCores = offline ? 0 : clientInfo.NumUsedCores,
@@ -137,10 +138,9 @@
                     CpuUtilization = x.Where(y => y.SlaveState != DA.SlaveState.Offline).Average(y => (double?)y.CpuUtilization) ?? 0.0
                   });
-      var query = dimClientDao.GetAll()
-        .GroupBy(x => new { x.ResourceGroupId, x.GroupName })
-        .Select(x => new {
-          Id = x.Key.ResourceGroupId ?? default(Guid),
-          Name = x.Key.GroupName
-        });
+
+      var query = dimClientDao.GetAllOnlineSlaveGroups().Select(x => new {
+        Id = x.ResourceId,
+        Name = x.Name
+      });
       return pm.UseTransaction(() => new DT.GroupPage {
         TotalGroups = query.Count(),
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp/3.3/HeuristicLab.Services.WebApp-3.3.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp/3.3/HeuristicLab.Services.WebApp-3.3.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp/3.3/HeuristicLab.Services.WebApp-3.3.csproj	(revision 16240)
@@ -23,4 +23,5 @@
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
     <RestorePackages>true</RestorePackages>
+    <UseGlobalApplicationHostFile />
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -234,5 +235,4 @@
     <Content Include="WebApp\libs\bootstrap\fonts\glyphicons-halflings-regular.woff" />
     <Content Include="WebApp\libs\bootstrap\fonts\glyphicons-halflings-regular.woff2" />
-    <Content Include="WebApp\shared\directives\scrollToTop.js" />
     <Content Include="WebApp\shared\filter\filters.js" />
     <Content Include="WebApp\shared\images\HeuristicLab-Logo.png" />
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp/3.3/Web.config
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp/3.3/Web.config	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Services.WebApp/3.3/Web.config	(revision 16240)
@@ -4,6 +4,6 @@
     <remove name="HeuristicLab.Authentication" />
     <remove name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString" />
-    <add name="HeuristicLab.Authentication" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
-    <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.3" />
+    <add name="HeuristicLab.Authentication" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Authentication" />
+    <add name="HeuristicLab.Services.Hive.DataAccess.Settings.HeuristicLab_Hive_LinqConnectionString" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=HeuristicLab.Hive-3.4" />
   </connectionStrings>
   
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/VariableImpactCalculationTest.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/VariableImpactCalculationTest.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/VariableImpactCalculationTest.cs	(revision 16240)
@@ -0,0 +1,238 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Algorithms.ALPS;
+using HeuristicLab.Algorithms.DataAnalysis;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Problems.DataAnalysis.Symbolic;
+using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
+using HeuristicLab.Problems.Instances.DataAnalysis;
+using HeuristicLab.Selection;
+using HeuristicLab.Tests;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace HeuristicLab.Problems.DataAnalysis.Tests {
+
+  [TestClass()]
+  public class VariableImpactCalculationTest {
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis")]
+    [TestProperty("Time", "medium")]
+    public void RunAlpsSymRegFactoryVariableMibaC1Test() {
+      var alpsGA = CreateAlpsGaSymRegMibaC1Sample();
+      alpsGA.Start();
+      var ers = alpsGA.Results.FirstOrDefault(v => v.Name == "Variable impacts").Value as DoubleMatrix;
+      Assert.IsNotNull(ers);
+      Assert.IsTrue(ers.Rows == 22);
+      List<string> expectedVariableOrder = new List<string> { "Oil", "Grooving", "Material_Cat", "x20", "Material", "x14", "x12", "x3", "x7", "x2", "x16", "x15", "x8", "x10", "x11", "x22", "x4", "x5", "x6", "x9", "x17", "x13" };
+      List<double> expectedVariableImpacts = new List<double> { 0.206, 0.179, 0.136, 0.099, 0.092, 0.07, 0.048, 0.033, 0.029, 0.026, 0.017, 0.01, 0.007, 0.007, 0.007, 0.006, 0.005, 0.005, 0.005, 0.005, 0.005, 0.003 };
+      Assert.IsTrue(ers.RowNames.SequenceEqual(expectedVariableOrder));
+      Assert.IsTrue(ers.SequenceEqual(expectedVariableImpacts));
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis")]
+    [TestProperty("Time", "medium")]
+    public void RunAlpsSymRegFactoryVariableMibaWear1Test() {
+      var alpsGA = CreateAlpsGaSymRegMibaWear1Sample();
+      alpsGA.Start();
+      var ers = alpsGA.Results.FirstOrDefault(v => v.Name == "Variable impacts").Value as DoubleMatrix;
+      Assert.IsNotNull(ers);
+      Assert.IsTrue(ers.Rows == 27);
+      List<string> expectedVariableOrder = new List<string> { "Oil", "Material_Cat", "Material", "Grooving", "x8", "x1", "x14", "x11", "x19", "x10", "x5", "x9", "x17", "x6", "x18", "x7", "Source1", "x2", "x3", "x4", "x16", "x20", "x12", "x13", "x15", "x22", "x21" };
+      List<double> expectedVariableImpacts = new List<double> { 0.194, 0.184, 0.163, 0.155, 0.022, 0.019, 0.019, 0.018, 0.017, 0.016, 0.015, 0.015, 0.015, 0.014, 0.014, 0.013, 0.012, 0.011, 0.011, 0.011, 0.011, 0.011, 0.01, 0.009, 0.009, 0.008, 0.006 };
+      Assert.IsTrue(ers.RowNames.SequenceEqual(expectedVariableOrder));
+      Assert.IsTrue(ers.SequenceEqual(expectedVariableImpacts));
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis")]
+    [TestProperty("Time", "medium")]
+    public void RunAlpsSymRegFactoryVariableRealworldTowerTest() {
+      var alpsGA = CreateAlpsGaSymReRealworldTowerSample();
+      alpsGA.Start();
+      var ers = alpsGA.Results.FirstOrDefault(v => v.Name == "Variable impacts").Value as DoubleMatrix;
+      Assert.IsNotNull(ers);
+      Assert.IsTrue(ers.Rows == 25);
+      List<string> expectedVariableOrder = new List<string> { "x5", "x4", "x21", "x22", "x13", "x3", "x9", "x19", "x1", "x12", "x8", "x10", "x25", "x11", "x23", "x7", "x24", "x6", "x14", "x15", "x20", "x2", "x16", "x18", "x17" };
+      List<double> expectedVariableImpacts = new List<double> { 0.057, 0.051, 0.047, 0.047, 0.046, 0.045, 0.044, 0.044, 0.043, 0.043, 0.041, 0.04, 0.04, 0.038, 0.038, 0.037, 0.037, 0.036, 0.036, 0.034, 0.033, 0.031, 0.031, 0.031, 0.03 };
+      Assert.IsTrue(ers.RowNames.SequenceEqual(expectedVariableOrder));
+      Assert.IsTrue(ers.SequenceEqual(expectedVariableImpacts));
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void RunRandomForestPolyTenTest() {
+      var randomForestRegression = new RandomForestRegression() {
+        M = 0.4,
+        R = 0.3,
+        NumberOfTrees = 50,
+        SetSeedRandomly = false,
+        Seed = 42
+      };
+
+      var symbRegProblem = new SymbolicRegressionSingleObjectiveProblem();
+      symbRegProblem.ProblemData = new PolyTen(555000).GenerateRegressionData();
+      randomForestRegression.Problem = symbRegProblem;
+      randomForestRegression.Start();
+
+      var rfs = randomForestRegression.Results.FirstOrDefault(v => v.Name == "Random forest regression solution").Value as RandomForestRegressionSolution;
+      Assert.IsNotNull(rfs);
+      var result = new RegressionSolutionVariableImpactsCalculator().Calculate(rfs);
+
+      //Set expected impacts
+      List<Tuple<string, double>> aux = new List<Tuple<string, double>>();
+      aux.Add(new Tuple<string, double>("X6", 0.14984398650032071));
+      aux.Add(new Tuple<string, double>("X5", 0.14361275357221248));
+      aux.Add(new Tuple<string, double>("X1", 0.10607502238071009));
+      aux.Add(new Tuple<string, double>("X3", 0.1056849194554953));
+      aux.Add(new Tuple<string, double>("X4", 0.0906750779077492));
+      aux.Add(new Tuple<string, double>("X2", 0.089817766590950532));
+      aux.Add(new Tuple<string, double>("X8", 0.042797597332305481));
+      aux.Add(new Tuple<string, double>("X9", 0.038609311722408446));
+      aux.Add(new Tuple<string, double>("X10", 0.036202503161574362));
+      aux.Add(new Tuple<string, double>("X7", 0.033539038256832465));
+      Assert.IsTrue(result.SequenceEqual(aux));
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void RunLinearRegressionBreimanITest() {
+      LinearRegression lr = new LinearRegression();
+      var symbRegProblem = new SymbolicRegressionSingleObjectiveProblem();
+      symbRegProblem.ProblemData = new BreimanOne(1234).GenerateRegressionData();
+
+      lr.Problem = symbRegProblem;
+      lr.Start();
+
+      var lrs = lr.Results.FirstOrDefault(v => v.Name == "Linear regression solution").Value as SymbolicRegressionSolution;
+      Assert.IsNotNull(lrs);
+      var result = new RegressionSolutionVariableImpactsCalculator().Calculate(lrs);
+
+      //Set expected impacts
+      List<Tuple<string, double>> aux = new List<Tuple<string, double>>();
+      aux.Add(new Tuple<string, double>("X1", 0.43328518823918716));
+      aux.Add(new Tuple<string, double>("X2", 0.073284548674773631));
+      aux.Add(new Tuple<string, double>("X5", 0.070306657566311159));
+      aux.Add(new Tuple<string, double>("X3", 0.035352205426012917));
+      aux.Add(new Tuple<string, double>("X6", 0.031710492680145475));
+      aux.Add(new Tuple<string, double>("X4", 0.0081659530036176653));
+      aux.Add(new Tuple<string, double>("X7", 0.0070869550705255913));
+      aux.Add(new Tuple<string, double>("X9", 8.0546619615096127E-05));
+      aux.Add(new Tuple<string, double>("X8", 6.2072921147349192E-05));
+      aux.Add(new Tuple<string, double>("X10", 1.9113559758254794E-06));
+
+      Assert.IsTrue(result.SequenceEqual(aux));
+    }
+
+    private AlpsGeneticAlgorithm CreateAlpsGaSymRegMibaC1Sample() {
+      AlpsGeneticAlgorithm alpsGa = new AlpsGeneticAlgorithm();
+      #region Problem Configuration
+      var provider = new MibaFrictionRegressionInstanceProvider();
+      var instance = provider.GetDataDescriptors().Single(x => x.Name.StartsWith("CF1"));
+      var symbRegProblem = new SymbolicRegressionSingleObjectiveProblem();
+      symbRegProblem.Load(provider.LoadData(instance));
+
+      symbRegProblem.MaximumSymbolicExpressionTreeDepth.Value = 35;
+      symbRegProblem.MaximumSymbolicExpressionTreeLength.Value = 35;
+
+      var grammar = (TypeCoherentExpressionGrammar)symbRegProblem.SymbolicExpressionTreeGrammar;
+      grammar.Symbols.OfType<Exponential>().Single().Enabled = false;
+      grammar.Symbols.OfType<Logarithm>().Single().Enabled = false;
+
+      #endregion
+      #region Algorithm Configuration
+      alpsGa.Name = "ALPS Genetic Programming - Symbolic Regression";
+      alpsGa.Description = "An ALPS-GP to solve a symbolic regression problem";
+      alpsGa.Problem = symbRegProblem;
+      SamplesUtils.ConfigureAlpsGeneticAlgorithmParameters<GeneralizedRankSelector, SubtreeCrossover, MultiSymbolicExpressionTreeManipulator>(alpsGa,
+        numberOfLayers: 1000,
+        popSize: 100,
+        mutationRate: 0.25,
+        elites: 1,
+        plusSelection: false,
+        agingScheme: AgingScheme.Polynomial,
+        ageGap: 15,
+        ageInheritance: 1.0,
+        maxGens: 10);
+
+      alpsGa.SetSeedRandomly.Value = false;
+      alpsGa.Seed.Value = 1234;
+      #endregion
+      return alpsGa;
+    }
+    private AlpsGeneticAlgorithm CreateAlpsGaSymRegMibaWear1Sample() {
+      AlpsGeneticAlgorithm alpsGa = new AlpsGeneticAlgorithm();
+      #region Problem Configuration
+      var provider = new MibaFrictionRegressionInstanceProvider();
+      var instance = provider.GetDataDescriptors().Single(x => x.Name.StartsWith("Wear1"));
+      var symbRegProblem = new SymbolicRegressionSingleObjectiveProblem();
+      symbRegProblem.Load(provider.LoadData(instance));
+
+      symbRegProblem.MaximumSymbolicExpressionTreeDepth.Value = 77;
+      symbRegProblem.MaximumSymbolicExpressionTreeLength.Value = 77;
+
+      var grammar = (TypeCoherentExpressionGrammar)symbRegProblem.SymbolicExpressionTreeGrammar;
+
+      #endregion
+      #region Algorithm Configuration
+      alpsGa.Name = "ALPS Genetic Programming - Symbolic Regression";
+      alpsGa.Description = "An ALPS-GP to solve a symbolic regression problem";
+      alpsGa.Problem = symbRegProblem;
+      SamplesUtils.ConfigureAlpsGeneticAlgorithmParameters<RandomSelector, SubtreeCrossover, MultiSymbolicExpressionTreeManipulator>(alpsGa,
+        numberOfLayers: 1000,
+        popSize: 200,
+        mutationRate: 0.25,
+        elites: 1,
+        plusSelection: false,
+        agingScheme: AgingScheme.Polynomial,
+        ageGap: 15,
+        ageInheritance: 1.0,
+        maxGens: 10);
+
+      alpsGa.SetSeedRandomly.Value = false;
+      alpsGa.Seed.Value = 11121314;
+      #endregion
+      return alpsGa;
+    }
+    private AlpsGeneticAlgorithm CreateAlpsGaSymReRealworldTowerSample() {
+      AlpsGeneticAlgorithm alpsGa = new AlpsGeneticAlgorithm();
+      #region Problem Configuration
+      var provider = new RegressionRealWorldInstanceProvider();
+      var instance = provider.GetDataDescriptors().Single(x => x.Name.StartsWith("Tower"));
+      var symbRegProblem = new SymbolicRegressionSingleObjectiveProblem();
+      symbRegProblem.Load(provider.LoadData(instance));
+
+      symbRegProblem.MaximumSymbolicExpressionTreeDepth.Value = 77;
+      symbRegProblem.MaximumSymbolicExpressionTreeLength.Value = 77;
+
+      var grammar = (TypeCoherentExpressionGrammar)symbRegProblem.SymbolicExpressionTreeGrammar;
+
+      #endregion
+      #region Algorithm Configuration
+      alpsGa.Name = "ALPS Genetic Programming - Symbolic Regression";
+      alpsGa.Description = "An ALPS-GP to solve a symbolic regression problem";
+      alpsGa.Problem = symbRegProblem;
+      SamplesUtils.ConfigureAlpsGeneticAlgorithmParameters<RandomSelector, SubtreeCrossover, MultiSymbolicExpressionTreeManipulator>(alpsGa,
+        numberOfLayers: 1000,
+        popSize: 200,
+        mutationRate: 0.25,
+        elites: 1,
+        plusSelection: false,
+        agingScheme: AgingScheme.Polynomial,
+        ageGap: 15,
+        ageInheritance: 1.0,
+        maxGens: 10);
+
+      alpsGa.SetSeedRandomly.Value = false;
+      alpsGa.Seed.Value = 1111;
+      #endregion
+      return alpsGa;
+    }
+
+    //TODO: Add Function-Tests once the Branch of #2904 is done
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/DeriveTest.cs
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/DeriveTest.cs	(revision 16240)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/DeriveTest.cs	(revision 16240)
@@ -0,0 +1,88 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+
+using System;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Tests {
+
+
+  [TestClass]
+  public class DerivativeTest {
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis.Symbolic")]
+    [TestProperty("Time", "short")]
+    public void DeriveExpressions() {
+      var formatter = new InfixExpressionFormatter();
+      var parser = new InfixExpressionParser();
+      Assert.AreEqual("0", Derive("3", "x"));
+      Assert.AreEqual("1", Derive("x", "x"));
+      Assert.AreEqual("10", Derive("10*x", "x"));
+      Assert.AreEqual("10", Derive("x*10", "x"));
+      Assert.AreEqual("(2*'x')", Derive("x*x", "x"));
+      Assert.AreEqual("((('x' * 'x') * 2) + ('x' * 'x'))", Derive("x*x*x", "x")); // simplifier does not merge (x*x)*2 + x*x  to 3*x*x
+      Assert.AreEqual("0", Derive("10*x", "y"));
+      Assert.AreEqual("20", Derive("10*x+20*y", "y"));
+      Assert.AreEqual("6", Derive("2*3*x", "x"));
+      Assert.AreEqual("(10*'y')", Derive("10*x*y+20*y", "x"));
+      Assert.AreEqual("(1 / (SQR('x') * (-1)))",  Derive("1/x", "x"));
+      Assert.AreEqual("('y' / (SQR('x') * (-1)))", Derive("y/x", "x"));
+      Assert.AreEqual("((((-2*'x') + (-1)) * ('a' + 'b')) / SQR(('x' + ('x' * 'x'))))",
+        Derive("(a+b)/(x+x*x)", "x"));
+      Assert.AreEqual("((((-2*'x') + (-1)) * ('a' + 'b')) / SQR(('x' + SQR('x'))))", Derive("(a+b)/(x+SQR(x))", "x"));
+      Assert.AreEqual("EXP('x')", Derive("exp(x)", "x"));
+      Assert.AreEqual("(EXP((3*'x')) * 3)", Derive("exp(3*x)", "x"));
+      Assert.AreEqual("(1 / 'x')", Derive("log(x)", "x"));
+      Assert.AreEqual("(1 / 'x')", Derive("log(3*x)", "x"));   // 3 * 1/(3*x)
+      Assert.AreEqual("(1 / ('x' + (0.333333333333333*'y')))", Derive("log(3*x+y)", "x"));  // simplifier does not try to keep fractions
+      Assert.AreEqual("(1 / (SQRT(((3*'x') + 'y')) * 0.666666666666667))", Derive("sqrt(3*x+y)", "x"));   // 3 / (2 * sqrt(3*x+y)) = 1 / ((2/3) * sqrt(3*x+y)) 
+      Assert.AreEqual("(COS((3*'x')) * 3)", Derive("sin(3*x)", "x"));
+      Assert.AreEqual("(SIN((3*'x')) * (-3))", Derive("cos(3*x)", "x"));
+
+
+      // special case: Inv(x) using only one argument to the division symbol
+      // f(x) = 1/x
+      var root = new ProgramRootSymbol().CreateTreeNode();
+      var start = new StartSymbol().CreateTreeNode();
+      var div = new Division().CreateTreeNode();
+      var varNode = (VariableTreeNode)(new Variable().CreateTreeNode());
+      varNode.Weight = 1.0;
+      varNode.VariableName = "x";
+      div.AddSubtree(varNode);
+      start.AddSubtree(div);
+      root.AddSubtree(start);
+      var t = new SymbolicExpressionTree(root);
+      Assert.AreEqual("(1 / (SQR('x') * (-1)))", 
+        formatter.Format(DerivativeCalculator.Derive(t, "x")));
+    }
+
+    private string Derive(string expr, string variable) {
+      var parser = new InfixExpressionParser();
+      var formatter = new InfixExpressionFormatter();
+
+      var t = parser.Parse(expr);
+      var tPrime = DerivativeCalculator.Derive(t, variable);
+
+      return formatter.Format(tPrime);
+    }
+  }
+}
Index: /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Tests.csproj
===================================================================
--- /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Tests.csproj	(revision 16239)
+++ /branches/2915-AbsoluteSymbol/HeuristicLab.Tests/HeuristicLab.Tests.csproj	(revision 16240)
@@ -586,4 +586,6 @@
     <Compile Include="HeuristicLab.Problems.DataAnalysis-3.4\OnlineCalculatorPerformanceTest.cs" />
     <Compile Include="HeuristicLab.Problems.DataAnalysis-3.4\StatisticCalculatorsTest.cs" />
+    <Compile Include="HeuristicLab.Problems.DataAnalysis-3.4\VariableImpactCalculationTest.cs" />
+    <Compile Include="HeuristicLab.Problems.DataAnalysis.Symbolic-3.4\DeriveTest.cs" />
     <Compile Include="HeuristicLab.Problems.DataAnalysis.Symbolic-3.4\InfixExpressionParserTest.cs" />
     <Compile Include="HeuristicLab.Problems.DataAnalysis.Symbolic-3.4\SymbolicExpressionTreeBottomUpSimilarityCalculatorTest.cs" />
