Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HiveHiveEngine/HeuristicLab.Clients.Hive/3.3/HiveClient.cs @ 9450

Last change on this file since 9450 was 7287, checked in by ascheibe, 13 years ago

#1744

  • added the new Hive engine. The engine can now be executed in the Hive. If child tasks are created it pauses and is transfered back to the server. If the child tasks are finished it is sent back to a slave.
  • changed the server so that it reschedules paused parent tasks if their childs are finished as well as tasks where FinishWhenChildJobsFinished is set to false
File size: 23.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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.IO;
25using System.Linq;
26using System.Security.Cryptography;
27using System.Threading;
28using System.Threading.Tasks;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Optimization;
32using HeuristicLab.PluginInfrastructure;
33using TS = System.Threading.Tasks;
34
35namespace HeuristicLab.Clients.Hive {
36  [Item("HiveClient", "Hive client.")]
37  public sealed class HiveClient : IContent {
38    private static HiveClient instance;
39    public static HiveClient Instance {
40      get {
41        if (instance == null) instance = new HiveClient();
42        return instance;
43      }
44    }
45
46    #region Properties
47    private ItemCollection<RefreshableJob> jobs;
48    public ItemCollection<RefreshableJob> Jobs {
49      get { return jobs; }
50      set {
51        if (value != jobs) {
52          jobs = value;
53          OnHiveExperimentsChanged();
54        }
55      }
56    }
57
58    private List<Plugin> onlinePlugins;
59    public List<Plugin> OnlinePlugins {
60      get { return onlinePlugins; }
61      set { onlinePlugins = value; }
62    }
63
64    private List<Plugin> alreadyUploadedPlugins;
65    public List<Plugin> AlreadyUploadedPlugins {
66      get { return alreadyUploadedPlugins; }
67      set { alreadyUploadedPlugins = value; }
68    }
69
70    private bool isAllowedPrivileged;
71    public bool IsAllowedPrivileged {
72      get { return isAllowedPrivileged; }
73      set { isAllowedPrivileged = value; }
74    }
75    #endregion
76
77    private HiveClient() { }
78
79    #region Refresh
80    public void Refresh() {
81      OnRefreshing();
82
83      try {
84        this.IsAllowedPrivileged = HiveServiceLocator.Instance.CallHiveService((s) => s.IsAllowedPrivileged());
85
86        var oldJobs = jobs ?? new ItemCollection<RefreshableJob>();
87        jobs = new HiveItemCollection<RefreshableJob>();
88        var jobsLoaded = HiveServiceLocator.Instance.CallHiveService<IEnumerable<Job>>(s => s.GetJobs());
89
90        foreach (var j in jobsLoaded) {
91          var job = oldJobs.SingleOrDefault(x => x.Id == j.Id);
92          if (job == null) {
93            // new
94            jobs.Add(new RefreshableJob(j) { IsAllowedPrivileged = this.isAllowedPrivileged });
95          } else {
96            // update
97            job.Job = j;
98            job.IsAllowedPrivileged = this.isAllowedPrivileged;
99            jobs.Add(job);
100          }
101        }
102        // remove those which were not in the list of loaded hiveexperiments
103        foreach (var job in oldJobs) {
104          if (job.Id == Guid.Empty) {
105            // experiment not uploaded... keep
106            jobs.Add(job);
107          } else {
108            job.RefreshAutomatically = false; // stop results polling
109          }
110        }
111      }
112      catch {
113        jobs = null;
114        throw;
115      }
116      finally {
117        OnRefreshed();
118      }
119    }
120    public void RefreshAsync(Action<Exception> exceptionCallback) {
121      var call = new Func<Exception>(delegate() {
122        try {
123          Refresh();
124        }
125        catch (Exception ex) {
126          return ex;
127        }
128        return null;
129      });
130      call.BeginInvoke(delegate(IAsyncResult result) {
131        Exception ex = call.EndInvoke(result);
132        if (ex != null) exceptionCallback(ex);
133      }, null);
134    }
135    #endregion
136
137    #region Store
138    public static void Store(IHiveItem item, CancellationToken cancellationToken) {
139      if (item.Id == Guid.Empty) {
140        if (item is RefreshableJob) {
141          HiveClient.Instance.UploadJob((RefreshableJob)item, cancellationToken);
142        }
143        if (item is JobPermission) {
144          var hep = (JobPermission)item;
145          hep.GrantedUserId = HiveServiceLocator.Instance.CallHiveService((s) => s.GetUserIdByUsername(hep.GrantedUserName));
146          if (hep.GrantedUserId == Guid.Empty) {
147            throw new ArgumentException(string.Format("The user {0} was not found.", hep.GrantedUserName));
148          }
149          HiveServiceLocator.Instance.CallHiveService((s) => s.GrantPermission(hep.JobId, hep.GrantedUserId, hep.Permission));
150        }
151      } else {
152        if (item is Job)
153          HiveServiceLocator.Instance.CallHiveService(s => s.UpdateJob((Job)item));
154      }
155    }
156    public static void StoreAsync(Action<Exception> exceptionCallback, IHiveItem item, CancellationToken cancellationToken) {
157      var call = new Func<Exception>(delegate() {
158        try {
159          Store(item, cancellationToken);
160        }
161        catch (Exception ex) {
162          return ex;
163        }
164        return null;
165      });
166      call.BeginInvoke(delegate(IAsyncResult result) {
167        Exception ex = call.EndInvoke(result);
168        if (ex != null) exceptionCallback(ex);
169      }, null);
170    }
171    #endregion
172
173    #region Delete
174    public static void Delete(IHiveItem item) {
175      if (item.Id == Guid.Empty && item.GetType() != typeof(JobPermission))
176        return;
177
178      if (item is Job)
179        HiveServiceLocator.Instance.CallHiveService(s => s.DeleteJob(item.Id));
180      if (item is RefreshableJob) {
181        RefreshableJob job = (RefreshableJob)item;
182        if (job.RefreshAutomatically) {
183          job.StopResultPolling();
184        }
185        HiveServiceLocator.Instance.CallHiveService(s => s.DeleteJob(item.Id));
186      }
187      if (item is JobPermission) {
188        var hep = (JobPermission)item;
189        HiveServiceLocator.Instance.CallHiveService(s => s.RevokePermission(hep.JobId, hep.GrantedUserId));
190      }
191      item.Id = Guid.Empty;
192    }
193    #endregion
194
195    #region Events
196    public event EventHandler Refreshing;
197    private void OnRefreshing() {
198      EventHandler handler = Refreshing;
199      if (handler != null) handler(this, EventArgs.Empty);
200    }
201    public event EventHandler Refreshed;
202    private void OnRefreshed() {
203      var handler = Refreshed;
204      if (handler != null) handler(this, EventArgs.Empty);
205    }
206    public event EventHandler HiveExperimentsChanged;
207    private void OnHiveExperimentsChanged() {
208      var handler = HiveExperimentsChanged;
209      if (handler != null) handler(this, EventArgs.Empty);
210    }
211    #endregion
212
213    public static void StartJob(Action<Exception> exceptionCallback, RefreshableJob refreshableJob, CancellationToken cancellationToken) {
214      HiveClient.StoreAsync(
215        new Action<Exception>((Exception ex) => {
216          refreshableJob.ExecutionState = ExecutionState.Prepared;
217          exceptionCallback(ex);
218        }), refreshableJob, cancellationToken);
219      refreshableJob.ExecutionState = ExecutionState.Started;
220    }
221
222    public static void PauseJob(RefreshableJob refreshableJob) {
223      HiveServiceLocator.Instance.CallHiveService(service => {
224        foreach (HiveTask task in refreshableJob.GetAllHiveTasks()) {
225          if (task.Task.State != TaskState.Finished && task.Task.State != TaskState.Aborted && task.Task.State != TaskState.Failed)
226            service.PauseTask(task.Task.Id);
227        }
228      });
229      refreshableJob.ExecutionState = ExecutionState.Paused;
230    }
231
232    public static void StopJob(RefreshableJob refreshableJob) {
233      HiveServiceLocator.Instance.CallHiveService(service => {
234        foreach (HiveTask task in refreshableJob.GetAllHiveTasks()) {
235          if (task.Task.State != TaskState.Finished && task.Task.State != TaskState.Aborted && task.Task.State != TaskState.Failed)
236            service.StopTask(task.Task.Id);
237        }
238      });
239      refreshableJob.ExecutionState = ExecutionState.Stopped;
240    }
241
242    public static void ResumeJob(RefreshableJob refreshableJob) {
243      HiveServiceLocator.Instance.CallHiveService(service => {
244        foreach (HiveTask task in refreshableJob.GetAllHiveTasks()) {
245          if (task.Task.State == TaskState.Paused) {
246            service.RestartTask(task.Task.Id);
247          }
248        }
249      });
250      refreshableJob.ExecutionState = ExecutionState.Started;
251    }
252
253    #region Upload Job
254    private Semaphore taskUploadSemaphore = new Semaphore(Settings.Default.MaxParallelUploads, Settings.Default.MaxParallelUploads);
255    private static object jobCountLocker = new object();
256    private static object pluginLocker = new object();
257    private void UploadJob(RefreshableJob refreshableJob, CancellationToken cancellationToken) {
258      try {
259        refreshableJob.Progress = new Progress("Connecting to server...");
260        refreshableJob.IsProgressing = true;
261
262        IEnumerable<string> resourceNames = ToResourceNameList(refreshableJob.Job.ResourceNames);
263        var resourceIds = new List<Guid>();
264        foreach (var resourceName in resourceNames) {
265          Guid resourceId = HiveServiceLocator.Instance.CallHiveService((s) => s.GetResourceId(resourceName));
266          if (resourceId == Guid.Empty) {
267            throw new ResourceNotFoundException(string.Format("Could not find the resource '{0}'", resourceName));
268          }
269          resourceIds.Add(resourceId);
270        }
271
272        foreach (OptimizerHiveTask hiveJob in refreshableJob.HiveTasks.OfType<OptimizerHiveTask>()) {
273          hiveJob.SetIndexInParentOptimizerList(null);
274        }
275
276        // upload Job
277        refreshableJob.Progress.Status = "Uploading Job...";
278        refreshableJob.Job.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddJob(refreshableJob.Job));
279        bool isPrivileged = refreshableJob.Job.IsPrivileged;
280        refreshableJob.Job = HiveServiceLocator.Instance.CallHiveService((s) => s.GetJob(refreshableJob.Job.Id)); // update owner and permissions
281        refreshableJob.Job.IsPrivileged = isPrivileged;
282        cancellationToken.ThrowIfCancellationRequested();
283
284        int totalJobCount = refreshableJob.GetAllHiveTasks().Count();
285        int[] jobCount = new int[1]; // use a reference type (int-array) instead of value type (int) in order to pass the value via a delegate to task-parallel-library
286        cancellationToken.ThrowIfCancellationRequested();
287
288        // upload plugins
289        refreshableJob.Progress.Status = "Uploading plugins...";
290        this.OnlinePlugins = HiveServiceLocator.Instance.CallHiveService((s) => s.GetPlugins());
291        this.AlreadyUploadedPlugins = new List<Plugin>();
292        Plugin configFilePlugin = HiveServiceLocator.Instance.CallHiveService((s) => UploadConfigurationFile(s, onlinePlugins));
293        this.alreadyUploadedPlugins.Add(configFilePlugin);
294        cancellationToken.ThrowIfCancellationRequested();
295
296        if (refreshableJob.RefreshAutomatically) refreshableJob.StartResultPolling();
297
298        // upload tasks
299        refreshableJob.Progress.Status = "Uploading tasks...";
300
301        var tasks = new List<TS.Task>();
302        foreach (HiveTask hiveTask in refreshableJob.HiveTasks) {
303          tasks.Add(TS.Task.Factory.StartNew((hj) => {
304            UploadTaskWithChildren(refreshableJob.Progress, (HiveTask)hj, null, resourceIds, jobCount, totalJobCount, configFilePlugin.Id, refreshableJob.Job.Id, refreshableJob.Log, refreshableJob.Job.IsPrivileged, cancellationToken);
305          }, hiveTask)
306          .ContinueWith((x) => refreshableJob.Log.LogException(x.Exception), TaskContinuationOptions.OnlyOnFaulted));
307        }
308        try {
309          TS.Task.WaitAll(tasks.ToArray());
310        }
311        catch (AggregateException ae) {
312          if (!ae.InnerExceptions.All(e => e is TaskCanceledException)) throw ae; // for some reason the WaitAll throws a AggregateException containg a TaskCanceledException. i don't know where it comes from, however the tasks all finish properly, so for now just ignore it
313        }
314        refreshableJob.Job.Modified = false;
315      }
316      finally {
317        refreshableJob.IsProgressing = false;
318        refreshableJob.Progress.Finish();
319      }
320    }
321
322    /// <summary>
323    /// Uploads the local configuration file as plugin
324    /// </summary>
325    private static Plugin UploadConfigurationFile(IHiveService service, List<Plugin> onlinePlugins) {
326      string configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Settings.Default.HLBinaryName + ".config");
327      string configFileName = Settings.Default.HLBinaryName + ".config";
328      byte[] hash;
329
330      byte[] data = File.ReadAllBytes(configFilePath);
331      using (SHA1 sha1 = SHA1.Create()) {
332        hash = sha1.ComputeHash(data);
333      }
334
335      Plugin configPlugin = new Plugin() { Name = "Configuration", Version = new Version(), Hash = hash };
336      PluginData configFile = new PluginData() { FileName = configFileName, Data = data };
337
338      IEnumerable<Plugin> onlineConfig = onlinePlugins.Where(p => p.Hash.SequenceEqual(hash));
339
340      if (onlineConfig.Count() > 0) {
341        return onlineConfig.First();
342      } else {
343        configPlugin.Id = service.AddPlugin(configPlugin, new List<PluginData> { configFile });
344        return configPlugin;
345      }
346    }
347
348    /// <summary>
349    /// Uploads the given task and all its child-jobs while setting the proper parentJobId values for the childs
350    /// </summary>
351    /// <param name="parentHiveTask">shall be null if its the root task</param>
352    private void UploadTaskWithChildren(IProgress progress, HiveTask hiveTask, HiveTask parentHiveTask, IEnumerable<Guid> groups, int[] taskCount, int totalJobCount, Guid configPluginId, Guid jobId, ILog log, bool isPrivileged, CancellationToken cancellationToken) {
353      taskUploadSemaphore.WaitOne();
354      bool semaphoreReleased = false;
355      try {
356        cancellationToken.ThrowIfCancellationRequested();
357        lock (jobCountLocker) {
358          taskCount[0]++;
359        }
360        TaskData taskData;
361        List<IPluginDescription> plugins;
362
363        if (hiveTask.ItemTask.ComputeInParallel && (hiveTask.ItemTask.Item is Optimization.Experiment || hiveTask.ItemTask.Item is Optimization.BatchRun)) {
364          hiveTask.Task.IsParentTask = true;
365          hiveTask.Task.FinishWhenChildJobsFinished = true;
366          taskData = hiveTask.GetAsTaskData(true, out plugins);
367        } else {
368          if (hiveTask.ItemTask.Item is EngineAlgorithm && ((EngineAlgorithm)hiveTask.ItemTask.Item).Engine is IHiveEngine) {
369            hiveTask.Task.IsParentTask = true;
370            //TODO: this is a little hack so that this task does not get executed immediately because the taskdata will be uploaded twice
371            hiveTask.Task.State = TaskState.Aborted;
372          } else {
373            hiveTask.Task.IsParentTask = false;
374          }
375
376          hiveTask.Task.FinishWhenChildJobsFinished = false;
377          taskData = hiveTask.GetAsTaskData(false, out plugins);
378        }
379        cancellationToken.ThrowIfCancellationRequested();
380
381        TryAndRepeat(() => {
382          if (!cancellationToken.IsCancellationRequested) {
383            lock (pluginLocker) {
384              HiveServiceLocator.Instance.CallHiveService((s) => hiveTask.Task.PluginsNeededIds = PluginUtil.GetPluginDependencies(s, this.onlinePlugins, this.alreadyUploadedPlugins, plugins));
385            }
386          }
387        }, Settings.Default.MaxRepeatServiceCalls, "Failed to upload plugins");
388        cancellationToken.ThrowIfCancellationRequested();
389        hiveTask.Task.PluginsNeededIds.Add(configPluginId);
390        hiveTask.Task.JobId = jobId;
391        hiveTask.Task.IsPrivileged = isPrivileged;
392
393        log.LogMessage(string.Format("Uploading task ({0} kb, {1} objects)", taskData.Data.Count() / 1024, hiveTask.ItemTask.GetObjectGraphObjects().Count()));
394        TryAndRepeat(() => {
395          if (!cancellationToken.IsCancellationRequested) {
396            if (parentHiveTask != null) {
397              hiveTask.Task.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddChildTask(parentHiveTask.Task.Id, hiveTask.Task, taskData));
398            } else {
399              hiveTask.Task.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddTask(hiveTask.Task, taskData, groups.ToList()));
400
401              if (hiveTask.Task.State == TaskState.Aborted) {
402                //TODO: this is a very bad hack
403                if (hiveTask.ItemTask.Item is EngineAlgorithm && ((EngineAlgorithm)hiveTask.ItemTask.Item).Engine is IHiveEngine) {
404                  IHiveEngine he = ((EngineAlgorithm)hiveTask.ItemTask.Item).Engine as IHiveEngine;
405                  he.ParentTaskId = hiveTask.Task.Id;
406                  taskData = hiveTask.GetAsTaskData(false, out plugins);
407                  taskData.TaskId = hiveTask.Task.Id;
408
409                  Task t = HiveServiceLocator.Instance.CallHiveService((s) => s.GetTask(hiveTask.Task.Id));
410                  t.State = TaskState.Waiting;
411                  HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateTaskData(t, taskData));
412                }
413              }
414
415            }
416          }
417        }, Settings.Default.MaxRepeatServiceCalls, "Failed to add task", log);
418        cancellationToken.ThrowIfCancellationRequested();
419
420        lock (jobCountLocker) {
421          progress.ProgressValue = (double)taskCount[0] / totalJobCount;
422          progress.Status = string.Format("Uploaded task ({0} of {1})", taskCount[0], totalJobCount);
423        }
424
425        var tasks = new List<TS.Task>();
426        foreach (HiveTask child in hiveTask.ChildHiveTasks) {
427          tasks.Add(TS.Task.Factory.StartNew((tuple) => {
428            var arguments = (Tuple<HiveTask, HiveTask>)tuple;
429            UploadTaskWithChildren(progress, arguments.Item1, arguments.Item2, groups, taskCount, totalJobCount, configPluginId, jobId, log, isPrivileged, cancellationToken);
430          }, new Tuple<HiveTask, HiveTask>(child, hiveTask))
431          .ContinueWith((x) => log.LogException(x.Exception), TaskContinuationOptions.OnlyOnFaulted));
432        }
433        taskUploadSemaphore.Release(); semaphoreReleased = true; // the semaphore has to be release before waitall!
434        try {
435          TS.Task.WaitAll(tasks.ToArray());
436        }
437        catch (AggregateException ae) {
438          if (!ae.InnerExceptions.All(e => e is TaskCanceledException)) throw ae; // for some reason the WaitAll throws a AggregateException containg a TaskCanceledException. i don't know where it comes from, however the tasks all finish properly, so for now just ignore it
439        }
440      }
441      finally {
442        if (!semaphoreReleased) taskUploadSemaphore.Release();
443      }
444    }
445    #endregion
446
447    #region Download Experiment
448    public static void LoadJob(RefreshableJob refreshableJob) {
449      var hiveExperiment = refreshableJob.Job;
450      refreshableJob.Progress = new Progress();
451
452      try {
453        refreshableJob.IsProgressing = true;
454        int totalJobCount = 0;
455        IEnumerable<LightweightTask> allTasks;
456
457        refreshableJob.Progress.Status = "Connecting to Server...";
458        // fetch all task objects to create the full tree of tree of HiveTask objects
459        refreshableJob.Progress.Status = "Downloading list of tasks...";
460        allTasks = HiveServiceLocator.Instance.CallHiveService(s => s.GetLightweightJobTasks(hiveExperiment.Id));
461        totalJobCount = allTasks.Count();
462
463        refreshableJob.Progress.Status = "Downloading tasks...";
464        TaskDownloader downloader = new TaskDownloader(allTasks.Select(x => x.Id));
465        downloader.StartAsync();
466
467        while (!downloader.IsFinished) {
468          refreshableJob.Progress.ProgressValue = downloader.FinishedCount / (double)totalJobCount;
469          refreshableJob.Progress.Status = string.Format("Downloading/deserializing tasks... ({0}/{1} finished)", downloader.FinishedCount, totalJobCount);
470          Thread.Sleep(500);
471
472          if (downloader.IsFaulted) {
473            throw downloader.Exception;
474          }
475        }
476        IDictionary<Guid, HiveTask> allHiveTasks = downloader.Results;
477        var parents = allHiveTasks.Values.Where(x => !x.Task.ParentTaskId.HasValue);
478
479        refreshableJob.Progress.Status = "Downloading/deserializing complete. Displaying tasks...";
480        // build child-task tree
481        foreach (HiveTask hiveTask in parents) {
482          BuildHiveJobTree(hiveTask, allTasks, allHiveTasks);
483        }
484
485        refreshableJob.HiveTasks = new ItemCollection<HiveTask>(parents);
486        if (refreshableJob.IsFinished()) {
487          refreshableJob.ExecutionState = Core.ExecutionState.Stopped;
488        } else {
489          refreshableJob.ExecutionState = Core.ExecutionState.Started;
490        }
491        refreshableJob.OnLoaded();
492      }
493      finally {
494        refreshableJob.IsProgressing = false;
495      }
496    }
497
498    private static void BuildHiveJobTree(HiveTask parentHiveTask, IEnumerable<LightweightTask> allTasks, IDictionary<Guid, HiveTask> allHiveTasks) {
499      IEnumerable<LightweightTask> childTasks = from job in allTasks
500                                                where job.ParentTaskId.HasValue && job.ParentTaskId.Value == parentHiveTask.Task.Id
501                                                orderby job.DateCreated ascending
502                                                select job;
503      foreach (LightweightTask task in childTasks) {
504        HiveTask childHiveTask = allHiveTasks[task.Id];
505        parentHiveTask.AddChildHiveTask(childHiveTask);
506        BuildHiveJobTree(childHiveTask, allTasks, allHiveTasks);
507      }
508    }
509    #endregion
510
511    /// <summary>
512    /// Converts a string which can contain Ids separated by ';' to a enumerable
513    /// </summary>
514    private static IEnumerable<string> ToResourceNameList(string resourceNames) {
515      if (!string.IsNullOrEmpty(resourceNames)) {
516        return resourceNames.Split(';');
517      } else {
518        return new List<string>();
519      }
520    }
521
522    public static ItemTask LoadItemJob(Guid jobId) {
523      TaskData taskData = HiveServiceLocator.Instance.CallHiveService(s => s.GetTaskData(jobId));
524      try {
525        return PersistenceUtil.Deserialize<ItemTask>(taskData.Data);
526      }
527      catch {
528        return null;
529      }
530    }
531
532    /// <summary>
533    /// Executes the action. If it throws an exception it is repeated until repetition-count is reached.
534    /// If repetitions is -1, it is repeated infinitely.
535    /// </summary>
536    public static void TryAndRepeat(Action action, int repetitions, string errorMessage, ILog log = null) {
537      while (true) {
538        try { action(); return; }
539        catch (Exception e) {
540          if (repetitions == 0) throw new HiveException(errorMessage, e);
541          if (log != null) log.LogMessage(string.Format("{0}: {1} - will try again!", errorMessage, e.ToString()));
542          repetitions--;
543        }
544      }
545    }
546
547    public static HiveItemCollection<JobPermission> GetJobPermissions(Guid jobId) {
548      return HiveServiceLocator.Instance.CallHiveService((service) => {
549        IEnumerable<JobPermission> jps = service.GetJobPermissions(jobId);
550        foreach (var hep in jps) {
551          hep.UnmodifiedGrantedUserNameUpdate(service.GetUsernameByUserId(hep.GrantedUserId));
552        }
553        return new HiveItemCollection<JobPermission>(jps);
554      });
555    }
556  }
557}
Note: See TracBrowser for help on using the repository browser.