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

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