source: trunk/sources/HeuristicLab.Services.OKB/3.3/RunnerService.cs @ 4313

Last change on this file since 4313 was 4313, checked in by swagner, 12 years ago

Worked on OKB user authentication (#1167)

File size: 15.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Data.Linq;
25using System.Linq;
26using System.ServiceModel;
27using HeuristicLab.Services.OKB.DataAccess;
28using log4net;
29
30namespace HeuristicLab.Services.OKB {
31
32  /// <summary>
33  /// Implementation of the <see cref="IRunnerService"/>.
34  /// </summary>
35  [ServiceBehavior(
36    InstanceContextMode = InstanceContextMode.PerSession)]
37  public class RunnerService : IRunnerService, IDisposable {
38
39    private Guid sessionID;
40    private static ILog logger = LogManager.GetLogger(typeof(RunnerService));
41
42    private void Log(string message, params object[] args) {
43      using (log4net.ThreadContext.Stacks["NDC"].Push(sessionID.ToString())) {
44        logger.Info(String.Format(message, args));
45      }
46    }
47
48    /// <summary>
49    /// Initializes a new instance of the <see cref="RunnerService"/> class.
50    /// </summary>
51    public RunnerService() {
52      sessionID = Guid.NewGuid();
53      Log("Instantiating new service");
54    }
55
56    /// <summary>
57    /// Obtain a "starter kit" of information, containing:
58    /// <list>
59    /// <item>algorithm classes including algorithms</item>
60    /// <item>problem classes including problems</item>
61    /// <item>projects</item>
62    /// </list>
63    /// </summary>
64    public StarterKit GetStarterKit(string platformName) {
65      Log("distributing starter kit");
66      Platform platform;
67      using (OKBDataContext okb = new OKBDataContext()) {
68        try {
69          platform = okb.Platforms.Single(p => p.Name == platformName);
70        }
71        catch (InvalidOperationException) {
72          throw new FaultException(String.Format(
73            "Invalid platform name \"{0}\" available platforms are: \"{1}\"",
74            platformName,
75            string.Join("\", \"", okb.Platforms.Select(p => p.Name).ToArray())));
76        }
77      }
78      using (OKBDataContext okb = new OKBDataContext()) {
79        DataLoadOptions dlo = new DataLoadOptions();
80        dlo.LoadWith<AlgorithmClass>(ac => ac.Algorithms);
81        dlo.AssociateWith<AlgorithmClass>(ac => ac.Algorithms.Where(a => a.Platform == platform));
82        dlo.LoadWith<ProblemClass>(p => p.Problems);
83        dlo.AssociateWith<ProblemClass>(pc => pc.Problems.Where(p => p.Platform == platform));
84        okb.LoadOptions = dlo;
85        var StarterKit = new StarterKit() {
86          AlgorithmClasses = okb.AlgorithmClasses.ToList(),
87          ProblemClasses = okb.ProblemClasses.ToList(),
88          Projects = okb.Projects.ToList()
89        };
90        return StarterKit;
91      }
92    }
93
94    /// <summary>
95    /// Augument the given algorithm and problem entities with information
96    /// to conduct an experiment. This will add algorithm parameters and results and
97    /// problem characteristic values and solution representation as well as data
98    /// necessary for deserialization.
99    /// </summary>
100    public ExperimentKit PrepareExperiment(Algorithm algorithm, Problem problem) {
101      Log("preparing experiment: {0}@{1}", algorithm.Name, problem.Name);
102      OKBDataContext okb = new OKBDataContext();
103      try {
104        DataLoadOptions dlo = new DataLoadOptions();
105        dlo.LoadWith<Problem>(p => p.SolutionRepresentation);
106        dlo.LoadWith<Parameter>(p => p.DataType);
107        dlo.LoadWith<Result>(r => r.DataType);
108        dlo.LoadWith<Algorithm_Parameter>(ap => ap.Parameter);
109        dlo.LoadWith<Problem_Parameter>(pp => pp.Parameter);
110        dlo.LoadWith<Algorithm_Result>(ar => ar.Result);
111        okb.LoadOptions = dlo;
112        algorithm = okb.Algorithms.Single(a => a.Id == algorithm.Id);
113        problem = okb.Problems.Single(p => p.Id == problem.Id);
114        algorithm.Algorithm_Parameters.Load();
115        algorithm.Algorithm_Results.Load();
116        problem.IntProblemCharacteristicValues.Load();
117        problem.FloatProblemCharacteristicValues.Load();
118        problem.CharProblemCharacteristicValues.Load();
119        problem.Problem_Parameters.Load();
120        return new ExperimentKit() {
121          Algorithm = algorithm,
122          Problem = problem
123        };
124      }
125      catch (Exception x) {
126        Log("exception caught: " + x.ToString());
127        throw new FaultException("Excaption caught: " + x.ToString());
128      }
129    }
130
131    /// <summary>
132    /// Adds the a new <see cref="Experiment"/>
133    ///   <see cref="Run"/>.
134    /// The <see cref="Experiment"/> is created if necessary as well as
135    /// all <see cref="Parameter"/>s and <see cref="Result"/>s. If an
136    /// identical experiment has been conducted before the new run is
137    /// linked to this previous experiment instead.
138    /// </summary>
139    /// <param name="algorithm">The algorithm.</param>
140    /// <param name="problem">The problem.</param>
141    /// <param name="project">The project.</param>
142    public void AddRun(Algorithm algorithm, Problem problem, Project project) {
143      Log("adding run for {0}@{1}({2})[{3}, {4}]",
144        algorithm.Name, problem.Name, project.Name, currentUser.Name, currentClient.Name);
145      try {
146        using (OKBDataContext okb = new OKBDataContext()) {
147          Experiment experiment = GetOrCreateExperiment(algorithm, problem, project, currentUser, okb);
148          Run run = new Run() {
149            Experiment = experiment,
150            UserId = currentUser.Id,
151            ClientId = currentClient.Id,
152            FinishedDate = DateTime.Now,
153            ResultValues = algorithm.ResultValues
154          };
155          okb.Runs.InsertOnSubmit(run);
156          okb.SubmitChanges();
157        }
158      }
159      catch (Exception x) {
160        Log(x.ToString());
161        throw new FaultException("Could not add run: " + x.ToString());
162      }
163    }
164
165    private Experiment GetOrCreateExperiment(Algorithm algorithm, Problem problem,
166        Project project, User user, OKBDataContext okb) {
167      MatchResults(algorithm.Results, okb);
168      EnsureParametersExist(algorithm.Parameters, okb);
169      var experimentQuery = CreateExperimentQuery(algorithm, problem, project, okb);
170      if (experimentQuery.Count() > 0) {
171        if (experimentQuery.Count() > 1)
172          Log("Warning: duplicate experiment found");
173        Log("reusing existing experiment");
174        Experiment experiment = experimentQuery.First();
175        return experiment;
176      } else {
177        Log("creating new experiment");
178        Experiment experiment = new Experiment() {
179          AlgorithmId = algorithm.Id,
180          ProblemId = problem.Id,
181          ProjectId = project.Id,
182          ParameterValues = algorithm.ParameterValues
183        };
184        okb.Experiments.InsertOnSubmit(experiment);
185        return experiment;
186      }
187    }
188
189    private void MatchResults(IQueryable<Result> results, OKBDataContext okb) {
190      foreach (var result in results.Where(r => r.Name != null)) {
191        result.Id = okb.Results.Single(r => r.Name == result.Name).Id;
192        Log("mapping named result {0} -> Id {1}", result.Name, result.Id);
193        var value = result.ResultValue;
194        if (value != null) {
195          result.ResultValue = (IResultValue)Activator.CreateInstance(value.GetType());
196          result.ResultValue.Result = result;
197          result.ResultValue.Value = value.Value;
198        }
199      }
200    }
201
202    private void EnsureParametersExist(IQueryable<Parameter> parameters, OKBDataContext okb) {
203      foreach (var param in parameters.Where(p => p.DataType != null && p.DataType.ClrName != null)) {
204        DataType dataType = GetOrCreateDataType(okb, param.DataType.ClrName);
205        param.DataType = new DataType() { Id = dataType.Id };
206        Log("mapping datatype {0} to id {1}", dataType.ClrName, dataType.Id);
207      }
208      okb.SubmitChanges();
209      var namedParams = parameters.Where(p => p.Name != null);
210      var newParams = namedParams.Except(okb.Parameters, new NameComprarer());
211      foreach (var p in newParams) {
212        Log("creating new parameter {0} ({2}:{1})", p.Name, p.DataType.ClrName, p.DataType.Id);
213        okb.Parameters.InsertOnSubmit(new Parameter() {
214          Name = p.Name,
215          DataTypeId = p.DataTypeId,
216        });
217      }
218      okb.SubmitChanges();
219      foreach (var np in namedParams) {
220        np.Id = okb.Parameters.Single(p => p.Name == np.Name).Id;
221        Log("mapping named parameter {0} -> Id {1}", np.Name, np.Id);
222        var value = np.ParameterValue;
223        if (value != null) {
224          OperatorParameterValue opVal = value as OperatorParameterValue;
225          np.ParameterValue = (IParameterValue)Activator.CreateInstance(value.GetType());
226          np.ParameterValue.Parameter = np;
227          np.ParameterValue.Value = value.Value;
228          if (opVal != null) {
229            OperatorParameterValue newVal = np.ParameterValue as OperatorParameterValue;
230            DataType dataType = GetOrCreateDataType(okb, opVal.DataType.ClrName);
231            newVal.DataType = new DataType() { Id = dataType.Id };
232            Log("mapping operator parameter datatype {0} to id {1}", dataType.ClrName, dataType.Id);
233          }
234        }
235      }
236    }
237
238    private DataType GetOrCreateDataType(OKBDataContext okb, string clrName) {
239      DataType dataType = okb.DataTypes.SingleOrDefault(dt => dt.ClrName == clrName);
240      if (dataType == null) {
241        Log("creating new datatype for ", clrName);
242        dataType = new DataType() {
243          ClrName = clrName,
244          SqlName = "BLOB",
245        };
246        okb.DataTypes.InsertOnSubmit(dataType);
247        okb.SubmitChanges();
248      }
249      return dataType;
250    }
251
252    private IQueryable<Experiment> CreateExperimentQuery(Algorithm algorithm, Problem problem, Project project,
253        OKBDataContext okb) {
254      var experimentQuery =
255        from x in okb.Experiments
256        where x.Algorithm == algorithm
257        where x.Problem == problem
258        where x.ProjectId == project.Id
259        select x;
260      foreach (IntParameterValue ipv in algorithm.IntParameterValues) {
261        experimentQuery = experimentQuery
262          .Where(x => x.IntParameterValues.Any(p =>
263            p.ParameterId == ipv.ParameterId && p.Value == ipv.Value));
264      }
265      foreach (FloatParameterValue fpv in algorithm.FloatParameterValues) {
266        experimentQuery = experimentQuery
267          .Where(x => x.FloatParameterValues.Any(p =>
268            p.ParameterId == fpv.ParameterId && p.Value == fpv.Value));
269      }
270      foreach (CharParameterValue cpv in algorithm.CharParameterValues) {
271        experimentQuery = experimentQuery
272          .Where(x => x.CharParameterValues.Any(p =>
273            p.ParameterId == cpv.ParameterId && p.Value == cpv.Value));
274      }
275      foreach (OperatorParameterValue opv in algorithm.OperatorParameterValues) {
276        experimentQuery = experimentQuery
277          .Where(x => x.OperatorParameterValues.Any(p =>
278            p.ParameterId == opv.ParameterId && p.DataTypeId == opv.DataTypeId));
279      }
280      Log("experiment query: ", experimentQuery.Expression.ToString());
281      return experimentQuery;
282    }
283
284    /// <summary>
285    /// Determines whether this instance is connected.
286    /// </summary>
287    /// <returns>
288    ///   <c>true</c> if this instance is connected; otherwise, <c>false</c>.
289    /// </returns>
290    public bool IsConnected() {
291      return currentUser != null;
292    }
293
294    User currentUser = null;
295    Client currentClient = null;
296
297    /// <summary>
298    /// Logs the specified username in. In case the user or client
299    /// does not exist yet, they are created on the server. This
300    /// method is currently not used for authentication but merely
301    /// for auditing.
302    /// </summary>
303    /// <param name="username">The username.</param>
304    /// <param name="clientname">The clientname.</param>
305    /// <returns>
306    ///   <c>true</c> if the login was successful; <c>false</c> otherwise.
307    /// </returns>
308    public bool Login(string clientname) {
309      string username = ServiceSecurityContext.Current.PrimaryIdentity.Name;
310
311      Log("Authenticating {0}@{1}", username, clientname);
312      if (string.IsNullOrEmpty(username) ||
313          string.IsNullOrEmpty(clientname) ||
314          ServiceSecurityContext.Current.IsAnonymous) {
315        Log("rejecting anonymous login");
316        return false;
317      }
318      using (OKBDataContext okb = new OKBDataContext()) {
319        currentUser = okb.Users.SingleOrDefault(u => u.Name == username);
320        currentClient = okb.Clients.SingleOrDefault(c => c.Name == clientname);
321        if (currentUser == null) {
322          currentUser = new User() { Name = username, Id = Guid.NewGuid() };
323          okb.Users.InsertOnSubmit(currentUser);
324          okb.SubmitChanges();
325        }
326        if (currentClient == null) {
327          currentClient = new Client() { Name = clientname, Id = Guid.NewGuid() };
328          okb.Clients.InsertOnSubmit(currentClient);
329          okb.SubmitChanges();
330        }
331        Log("  user = {0}, client = {1}", currentUser, currentClient);
332        return true;
333      }
334    }
335
336    /// <summary>
337    /// Logout out and closes the connection.
338    /// </summary>
339    public void Logout() {
340      Log("Logging out");
341      currentUser = null;
342      currentClient = null;
343    }
344
345    /// <summary>
346    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
347    /// </summary>
348    public void Dispose() {
349      Log("Closing session...");
350      Logout();
351    }
352  }
353
354  /// <summary>
355  /// Compares two parameters by name.
356  /// </summary>
357  internal class NameComprarer : IEqualityComparer<Parameter> {
358
359    /// <summary>
360    /// Determines whether the specified objects are equal.
361    /// </summary>
362    /// <param name="x">The first object of type <paramref name="T"/> to compare.</param>
363    /// <param name="y">The second object of type <paramref name="T"/> to compare.</param>
364    /// <returns>
365    /// true if the specified objects are equal; otherwise, false.
366    /// </returns>
367    public bool Equals(Parameter x, Parameter y) {
368      return x.Name == y.Name;
369    }
370
371    /// <summary>
372    /// Returns a hash code for this instance.
373    /// </summary>
374    /// <param name="obj">The obj.</param>
375    /// <returns>
376    /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
377    /// </returns>
378    /// <exception cref="T:System.ArgumentNullException">
379    /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
380    /// </exception>
381    public int GetHashCode(Parameter obj) {
382      return obj.Name.GetHashCode();
383    }
384
385  }
386}
Note: See TracBrowser for help on using the repository browser.