Index: /trunk/sources/HeuristicLab.Grid/Database.cs
===================================================================
--- /trunk/sources/HeuristicLab.Grid/Database.cs	(revision 500)
+++ /trunk/sources/HeuristicLab.Grid/Database.cs	(revision 500)
@@ -0,0 +1,238 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 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.Text;
+using System.Data;
+using System.Data.SQLite;
+using System.Data.Common;
+using System.Threading;
+
+namespace HeuristicLab.Grid {
+  class Database {
+    private string connectionString;
+    private ReaderWriterLockSlim rwLock;
+    public Database(string connectionString) {
+      this.connectionString = connectionString;
+      rwLock = new ReaderWriterLockSlim();
+    }
+
+    #region create empty database
+    public void CreateNew() {
+      rwLock.EnterWriteLock();
+      try {
+        using(DbConnection cnn = new SQLiteConnection(connectionString)) {
+          cnn.Open();
+          using(DbTransaction t = cnn.BeginTransaction()) {
+            using(DbCommand cmd = cnn.CreateCommand()) {
+              cmd.CommandText = "CREATE TABLE Job (ID integer primary key autoincrement, Guid Guid, Status text, CreationTime DateTime, StartTime DateTime, RawData blob)";
+              cmd.Transaction = t;
+              cmd.ExecuteNonQuery();
+            }
+            t.Commit();
+          }
+        }
+      } finally {
+        rwLock.ExitWriteLock();
+      }
+    }
+    #endregion
+
+    internal void InsertJob(Guid guid, JobState jobState, byte[] rawData) {
+      rwLock.EnterWriteLock();
+      try {
+        using(SQLiteConnection cnn = new SQLiteConnection(connectionString)) {
+          cnn.Open();
+          using(DbTransaction t = cnn.BeginTransaction()) {
+            using(DbCommand c = cnn.CreateCommand()) {
+              c.Transaction = t;
+              c.CommandText = "Insert into Job (Guid, Status, CreationTime, StartTime, RawData) values (@Guid, @Status, @CreationTime, @StartTime, @RawData)";
+              DbParameter guidParam = c.CreateParameter();
+              guidParam.ParameterName = "@Guid";
+              guidParam.Value = guid;
+              c.Parameters.Add(guidParam);
+              DbParameter statusParam = c.CreateParameter();
+              statusParam.ParameterName = "@Status";
+              statusParam.Value = jobState.ToString();
+              c.Parameters.Add(statusParam);
+              DbParameter creationParam = c.CreateParameter();
+              creationParam.ParameterName = "@CreationTime";
+              DateTime now = DateTime.Now;
+              creationParam.Value = now;
+              c.Parameters.Add(creationParam);
+              DbParameter startParam = c.CreateParameter();
+              startParam.ParameterName = "@StartTime";
+              if(jobState == JobState.Busy) startParam.Value = now;
+              else startParam.Value = null;
+              c.Parameters.Add(startParam);
+              DbParameter rawDataParam = c.CreateParameter();
+              rawDataParam.ParameterName = "@RawData";
+              rawDataParam.Value = rawData;
+              c.Parameters.Add(rawDataParam);
+              c.ExecuteNonQuery();
+            }
+            t.Commit();
+          }
+        }
+      } finally {
+        rwLock.ExitWriteLock();
+      }
+
+    }
+
+    internal List<JobEntry> GetWaitingJobs() {
+      rwLock.EnterReadLock();
+      List<JobEntry> jobs = new List<JobEntry>();
+      try {
+        using(SQLiteConnection cnn = new SQLiteConnection(connectionString)) {
+          cnn.Open();
+          SQLiteCommand c = cnn.CreateCommand();
+          c.CommandText = "Select Guid, CreationTime, StartTime, Rawdata from Job where Status=@Status";
+          DbParameter statusParameter = c.CreateParameter();
+          statusParameter.ParameterName = "@Status";
+          statusParameter.Value = JobState.Waiting.ToString();
+          c.Parameters.Add(statusParameter);
+          SQLiteDataReader r = c.ExecuteReader();
+          while(r.Read()) {
+            JobEntry job = new JobEntry();
+            job.Status = JobState.Waiting;
+            job.Guid = r.GetGuid(0);
+            job.CreationTime = r.GetDateTime(1);
+            job.StartTime = r.IsDBNull(2)?null:new Nullable<DateTime>(r.GetDateTime(2));
+            job.RawData = (byte[])r.GetValue(3);
+            jobs.Add(job);
+          }
+        }
+      } finally {
+        rwLock.ExitReadLock();
+      }
+      return jobs;
+    }
+
+    internal void SetJobResult(Guid guid, byte[] result) {
+      rwLock.EnterWriteLock();
+      try {
+        using(SQLiteConnection cnn = new SQLiteConnection(connectionString)) {
+          cnn.Open();
+          using(SQLiteTransaction t = cnn.BeginTransaction()) {
+            using(SQLiteCommand c = cnn.CreateCommand()) {
+              c.Transaction = t;
+              c.CommandText = "Update Job set Status=@Status, RawData=@RawData where Guid=@Guid";
+              DbParameter rawDataParam = c.CreateParameter();
+              DbParameter statusParam = c.CreateParameter();
+              DbParameter guidParam = c.CreateParameter();
+              rawDataParam.ParameterName = "@RawData";
+              rawDataParam.Value = result;
+              guidParam.ParameterName = "@Guid";
+              guidParam.Value = guid;
+              statusParam.ParameterName = "@Status";
+              statusParam.Value = JobState.Finished;
+              c.Parameters.Add(rawDataParam);
+              c.Parameters.Add(statusParam);
+              c.Parameters.Add(guidParam);
+              c.ExecuteNonQuery();
+            }
+            t.Commit();
+          }
+        }
+      } finally {
+        rwLock.ExitWriteLock();
+      }
+    }
+
+    internal void UpdateJobState(Guid guid, JobState jobState) {
+      rwLock.EnterWriteLock();
+      try {
+        using(SQLiteConnection cnn = new SQLiteConnection(connectionString)) {
+          cnn.Open();
+          using(SQLiteTransaction t = cnn.BeginTransaction()) {
+            using(SQLiteCommand c = cnn.CreateCommand()) {
+              c.Transaction = t;
+              c.CommandText = "Update Job set Status=@Status, StartTime=@StartTime where Guid=@Guid";
+              DbParameter statusParam = c.CreateParameter();
+              DbParameter startTimeParam = c.CreateParameter();
+              DbParameter guidParam = c.CreateParameter();
+              startTimeParam.ParameterName = "@StartTime";
+              if(jobState == JobState.Busy)
+                startTimeParam.Value = DateTime.Now;
+              else
+                startTimeParam.Value = null;
+              guidParam.ParameterName = "@Guid";
+              guidParam.Value = guid;
+              statusParam.ParameterName = "@Status";
+              statusParam.Value = jobState;
+              c.Parameters.Add(startTimeParam);
+              c.Parameters.Add(statusParam);
+              c.Parameters.Add(guidParam);
+              c.ExecuteNonQuery();
+            }
+            t.Commit();
+          }
+        }
+      } finally {
+        rwLock.ExitWriteLock();
+      }
+    }
+
+
+    internal JobEntry GetJob(Guid guid) {
+      rwLock.EnterReadLock();
+      try {
+        using(SQLiteConnection cnn = new SQLiteConnection(connectionString)) {
+          cnn.Open();
+          SQLiteCommand c = cnn.CreateCommand();
+          c.CommandText = "Select Status, CreationTime, StartTime, Rawdata from Job where Guid=@Guid";
+          DbParameter guidParameter = c.CreateParameter();
+          guidParameter.ParameterName = "@Guid";
+          guidParameter.Value = guid;
+          c.Parameters.Add(guidParameter);
+          SQLiteDataReader r = c.ExecuteReader();
+          while(r.Read()) {
+            JobEntry job = new JobEntry();
+            job.Guid = guid;
+            job.Status = (JobState)Enum.Parse(typeof(JobState), r.GetString(0));
+            job.CreationTime = r.GetDateTime(1);
+            job.StartTime = r.IsDBNull(2)?null:new Nullable<DateTime>(r.GetDateTime(2));
+            job.RawData = (byte[])r.GetValue(3);
+            return job;
+          }
+        }
+      } finally {
+        rwLock.ExitReadLock();
+      }
+      return null;
+    }
+
+    /// <summary>
+    /// Does nothing right now (= running jobs that disappear are never restarted).
+    /// </summary>
+    internal void RestartExpiredActiveJobs() {
+    }
+
+    /// <summary>
+    /// Does nothing right now (= results are never deleted).
+    /// </summary>
+    internal void DeleteExpiredResults() {
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.Grid/EngineStore.cs
===================================================================
--- /trunk/sources/HeuristicLab.Grid/EngineStore.cs	(revision 499)
+++ /trunk/sources/HeuristicLab.Grid/EngineStore.cs	(revision 500)
@@ -23,62 +23,62 @@
 using System.Collections.Generic;
 using System.Text;
+using System.Linq;
 using System.Threading;
 using System.ServiceModel;
+using System.Data.Common;
 
 namespace HeuristicLab.Grid {
   [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
   public class EngineStore : IEngineStore {
-    private List<Guid> engineList;
-    private Dictionary<Guid, byte[]> waitingEngines;
-    private Dictionary<Guid, byte[]> runningEngines;
     private Dictionary<Guid, ManualResetEvent> waitHandles;
-    private Dictionary<Guid, byte[]> results;
-    private Dictionary<Guid, DateTime> resultDate;
-    private Dictionary<Guid, DateTime> runningEngineDate;
-    private const int RESULT_EXPIRY_TIME_MIN = 10;
-    private const int RUNNING_JOB_EXPIRY_TIME_MIN = 10;
-    private object bigLock;
+    private Database database;
+
+    private static readonly string dbFile = AppDomain.CurrentDomain.BaseDirectory + "/enginestore.db3";
+    private static readonly string connectionString = "Data Source=\"" + dbFile + "\";Pooling=False";
+
+    private int waitingJobs;
     public int WaitingJobs {
       get {
-        return waitingEngines.Count;
+        return waitingJobs;
       }
     }
-
+    private int runningJobs;
     public int RunningJobs {
       get {
-        return runningEngines.Count;
+        return runningJobs;
       }
     }
-
+    private int results;
     public int WaitingResults {
       get {
-        return results.Count;
+        return results;
       }
     }
 
     public EngineStore() {
-      engineList = new List<Guid>();
-      waitingEngines = new Dictionary<Guid, byte[]>();
-      runningEngines = new Dictionary<Guid, byte[]>();
       waitHandles = new Dictionary<Guid, ManualResetEvent>();
-      results = new Dictionary<Guid, byte[]>();
-      resultDate = new Dictionary<Guid, DateTime>();
-      runningEngineDate = new Dictionary<Guid, DateTime>();
-      bigLock = new object();
+      DbProviderFactory fact;
+      fact = DbProviderFactories.GetFactory("System.Data.SQLite");
+      if(!System.IO.File.Exists(dbFile)) {
+        database = new Database(connectionString);
+        database.CreateNew();
+      } else {
+        database = new Database(connectionString);
+      }
+      database = new Database(connectionString);
     }
 
+    private object t = new object();
     public bool TryTakeEngine(out Guid guid, out byte[] engine) {
-      lock(bigLock) {
-        if(engineList.Count == 0) {
-          guid = Guid.Empty;
-          engine = null;
+      lock(t) {
+        List<JobEntry> waitingJobs = database.GetWaitingJobs();
+        if(waitingJobs.Count == 0) {
+          guid = Guid.Empty; engine = null;
           return false;
         } else {
-          guid = engineList[0];
-          engineList.RemoveAt(0);
-          engine = waitingEngines[guid];
-          waitingEngines.Remove(guid);
-          runningEngines[guid] = engine;
-          runningEngineDate[guid] = DateTime.Now;
+          JobEntry oldestEntry = waitingJobs.OrderBy(a => a.CreationTime).First();
+          guid = oldestEntry.Guid;
+          engine = oldestEntry.RawData;
+          database.UpdateJobState(guid, HeuristicLab.Grid.JobState.Busy);
           return true;
         }
@@ -87,46 +87,13 @@
 
     public void StoreResult(Guid guid, byte[] result) {
-      lock(bigLock) {
-        // clear old results
-        List<Guid> expiredResults = FindExpiredResults(DateTime.Now.AddMinutes(-RESULT_EXPIRY_TIME_MIN));
-        foreach(Guid expiredGuid in expiredResults) {
-          results.Remove(expiredGuid);
-          waitHandles.Remove(expiredGuid);
-          resultDate.Remove(expiredGuid);
-        }
-        // add the new result
-        runningEngines.Remove(guid);
-        runningEngineDate.Remove(guid);
-        results[guid] = result;
-        resultDate[guid] = DateTime.Now;
-        waitHandles[guid].Set();
-      }
-    }
-
-    private List<Guid> FindExpiredResults(DateTime expirationDate) {
-      List<Guid> expiredResults = new List<Guid>();
-      foreach(Guid guid in results.Keys) {
-        if(resultDate[guid] < expirationDate) {
-          expiredResults.Add(guid);
-        }
-      }
-      return expiredResults;
-    }
-    private List<Guid> FindExpiredJobs(DateTime expirationDate) {
-      List<Guid> expiredJobs = new List<Guid>();
-      foreach(Guid guid in runningEngines.Keys) {
-        if(runningEngineDate[guid] < expirationDate) {
-          expiredJobs.Add(guid);
-        }
-      }
-      return expiredJobs;
+      database.DeleteExpiredResults();
+      // add the new result
+      database.SetJobResult(guid, result);
+      waitHandles[guid].Set();
     }
 
     internal void AddEngine(Guid guid, byte[] engine) {
-      lock(bigLock) {
-        engineList.Add(guid);
-        waitingEngines.Add(guid, engine);
-        waitHandles.Add(guid, new ManualResetEvent(false));
-      }
+      database.InsertJob(guid, HeuristicLab.Grid.JobState.Waiting, engine);
+      waitHandles.Add(guid, new ManualResetEvent(false));
     }
 
@@ -136,54 +103,32 @@
 
     internal byte[] GetResult(Guid guid, int timeout) {
-      lock(bigLock) {
-        // result already available
-        if(results.ContainsKey(guid)) {
-          // if the wait-handle for this result is still alive then close and remove it
-          if(waitHandles.ContainsKey(guid)) {
-            ManualResetEvent waitHandle = waitHandles[guid];
-            waitHandle.Close();
-            waitHandles.Remove(guid);
-          }
-          byte[] result = results[guid];
-          results.Remove(guid);
-          return result;
+      if(JobState(guid) == HeuristicLab.Grid.JobState.Finished) {
+        ManualResetEvent waitHandle = waitHandles[guid];
+        waitHandle.Close();
+        waitHandles.Remove(guid);
+        JobEntry entry = database.GetJob(guid);
+        return entry.RawData;
+      } else {
+        // result not yet available, if there is also no wait-handle for that result then we will never have a result and can return null
+        if(!waitHandles.ContainsKey(guid)) return null;
+        // otherwise we have a wait-handle and can wait for the result
+        ManualResetEvent waitHandle = waitHandles[guid];
+        // wait
+        if(waitHandle.WaitOne(timeout, true)) {
+          // ok got the result in within the wait time => close and remove the wait-hande and return the result
+          waitHandle.Close();
+          waitHandles.Remove(guid);
+          JobEntry entry = database.GetJob(guid);
+          return entry.RawData;
         } else {
-          // result not yet available, if there is also no wait-handle for that result then we will never have a result and can return null
-          if(!waitHandles.ContainsKey(guid)) return null;
-          // otherwise we have a wait-handle and can wait for the result
-          ManualResetEvent waitHandle = waitHandles[guid];
-          // wait
-          if(waitHandle.WaitOne(timeout, true)) {
-            // ok got the result in within the wait time => close and remove the wait-hande and return the result
-            waitHandle.Close();
-            waitHandles.Remove(guid);
-            byte[] result = results[guid];
-            return result;
-          } else {
-            // no result yet, check for which jobs we waited too long and requeue those jobs
-            List<Guid> expiredJobs = FindExpiredJobs(DateTime.Now.AddDays(-RUNNING_JOB_EXPIRY_TIME_MIN));
-            foreach(Guid expiredGuid in expiredJobs) {
-              engineList.Insert(0, expiredGuid);
-              waitingEngines[expiredGuid] = runningEngines[expiredGuid];
-              runningEngines.Remove(expiredGuid);
-              runningEngineDate.Remove(expiredGuid);
-            }
-            return null;
-          }
+          // no result yet, check for which jobs we waited too long and requeue those jobs
+          database.RestartExpiredActiveJobs();
+          return null;
         }
       }
     }
 
-    internal void AbortEngine(Guid guid) {
-      throw new NotImplementedException();
-    }
-
     internal JobState JobState(Guid guid) {
-      lock(bigLock) {
-        if(waitingEngines.ContainsKey(guid)) return HeuristicLab.Grid.JobState.Waiting;
-        else if(waitHandles.ContainsKey(guid)) return HeuristicLab.Grid.JobState.Busy;
-        else if(results.ContainsKey(guid)) return HeuristicLab.Grid.JobState.Finished;
-        else return HeuristicLab.Grid.JobState.Unknown;
-      }
+      return database.GetJob(guid).Status;
     }
   }
Index: /trunk/sources/HeuristicLab.Grid/GridServer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Grid/GridServer.cs	(revision 499)
+++ /trunk/sources/HeuristicLab.Grid/GridServer.cs	(revision 500)
@@ -51,8 +51,4 @@
       return engineStore.GetResult(guid, timeout);
     }
-
-    public void AbortEngine(Guid engine) {
-      engineStore.AbortEngine(engine);
-    }
   }
 }
Index: /trunk/sources/HeuristicLab.Grid/HeuristicLab.Grid.csproj
===================================================================
--- /trunk/sources/HeuristicLab.Grid/HeuristicLab.Grid.csproj	(revision 499)
+++ /trunk/sources/HeuristicLab.Grid/HeuristicLab.Grid.csproj	(revision 500)
@@ -39,4 +39,8 @@
       <RequiredTargetFramework>3.5</RequiredTargetFramework>
     </Reference>
+    <Reference Include="System.Data.SQLite, Version=1.0.51.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab.SQLite\System.Data.SQLite.DLL</HintPath>
+    </Reference>
     <Reference Include="System.Drawing" />
     <Reference Include="System.Runtime.Serialization">
@@ -60,4 +64,5 @@
       <DependentUpon>ClientForm.cs</DependentUpon>
     </Compile>
+    <Compile Include="Database.cs" />
     <Compile Include="EngineRunner.cs" />
     <Compile Include="EngineStore.cs" />
@@ -72,4 +77,5 @@
     <Compile Include="ProcessingEngine.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="JobEntry.cs" />
     <Compile Include="ServerForm.cs">
       <SubType>Form</SubType>
Index: /trunk/sources/HeuristicLab.Grid/IGridServer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Grid/IGridServer.cs	(revision 499)
+++ /trunk/sources/HeuristicLab.Grid/IGridServer.cs	(revision 500)
@@ -44,6 +44,4 @@
     [OperationContract]
     byte[] TryEndExecuteEngine(Guid engineGuid, int timeout);
-    [OperationContract]
-    void AbortEngine(Guid engineGuid);
   }
 }
Index: /trunk/sources/HeuristicLab.Grid/JobEntry.cs
===================================================================
--- /trunk/sources/HeuristicLab.Grid/JobEntry.cs	(revision 500)
+++ /trunk/sources/HeuristicLab.Grid/JobEntry.cs	(revision 500)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 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.Text;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Grid {
+  public class JobEntry {
+    public Guid Guid { get; set; }
+    public JobState Status { get; set; }
+    public DateTime CreationTime { get; set; }
+    public DateTime? StartTime { get; set; }
+    public byte[] RawData { get; set; }
+  }
+}
