Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Hive/sources/HeuristicLab.Hive/HeuristicLab.Hive.Experiment/3.3/HiveJob.cs @ 4550

Last change on this file since 4550 was 4424, checked in by cneumuel, 14 years ago
  • Added and updated License Information in every file
  • Sort and remove usings in every file
  • Deleted obsolete DataAccess.ADOHelper
  • Deleted some obsolete files
File size: 22.2 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.Drawing;
25using System.Linq;
26using HeuristicLab.Collections;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Hive.Contracts.BusinessObjects;
30using HeuristicLab.Hive.Experiment.Jobs;
31using HeuristicLab.Optimization;
32using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
33
34namespace HeuristicLab.Hive.Experiment {
35  [StorableClass]
36  public class HiveJob : Item {
37    private static object locker = new object();
38
39    public override Image ItemImage {
40      get {
41        if (jobDto.Id == Guid.Empty) { // not yet uploaded
42          return HeuristicLab.Common.Resources.VS2008ImageLibrary.Event;
43        } else {
44          if (jobDto.State == JobState.Offline) return HeuristicLab.Common.Resources.VS2008ImageLibrary.ExecutablePrepared;
45          else if (jobDto.State == JobState.WaitForChildJobs) return HeuristicLab.Common.Resources.VS2008ImageLibrary.ExecutablePrepared;
46          else if (jobDto.State == JobState.Calculating) return HeuristicLab.Common.Resources.VS2008ImageLibrary.ExecutableStarted;
47          else if (jobDto.State == JobState.Aborted) return HeuristicLab.Common.Resources.VS2008ImageLibrary.ExecutableStopped;
48          else if (jobDto.State == JobState.Failed) return HeuristicLab.Common.Resources.VS2008ImageLibrary.Error;
49          else if (jobDto.State == JobState.Finished) return HeuristicLab.Common.Resources.VS2008ImageLibrary.ExecutableStopped;
50          else return HeuristicLab.Common.Resources.VS2008ImageLibrary.Event;
51        }
52      }
53    }
54
55    [Storable]
56    private JobDto jobDto;
57    public JobDto JobDto {
58      get { return jobDto; }
59      set {
60        if (jobDto != value) {
61          jobDto = value;
62          OnJobDtoChanged();
63          OnToStringChanged();
64          OnItemImageChanged();
65        }
66      }
67    }
68
69    [Storable]
70    private OptimizerJob job;
71    public OptimizerJob Job {
72      get { return job; }
73      private set {
74        if (job != null && syncJobsWithOptimizers) {
75          this.childHiveJobs.Clear();
76        }
77        if (job != value) {
78          DergisterOptimizerEvents();
79          job = value;
80          if (job.ExecutionState == ExecutionState.Stopped) {
81            IsFinishedOptimizerDownloaded = true;
82          }
83          RegisterOptimizerEvents();
84          OnJobChanged();
85        }
86      }
87    }
88
89    [Storable]
90    private HiveJobList childHiveJobs;
91    public ReadOnlyItemList<HiveJob> ChildHiveJobs {
92      get { return childHiveJobs.AsReadOnly(); }
93    }
94
95    [Storable]
96    private bool isFinishedOptimizerDownloaded;
97    public bool IsFinishedOptimizerDownloaded {
98      get { return isFinishedOptimizerDownloaded; }
99      set {
100        if (isFinishedOptimizerDownloaded != value) {
101          isFinishedOptimizerDownloaded = value;
102          OnIsFinishedOptimizerDownloadedChanged();
103        }
104      }
105    }
106
107    [Storable]
108    private bool syncJobsWithOptimizers = true;
109
110    public HiveJob() {
111      this.JobDto = new JobDto() {
112        State = JobState.Offline,
113        DateCreated = DateTime.Now,
114        CoresNeeded = 1,
115        MemoryNeeded = 0
116      };
117      this.childHiveJobs = new HiveJobList();
118      syncJobsWithOptimizers = true;
119    }
120
121    public HiveJob(JobDto jobDto)
122      : this() {
123      this.JobDto = jobDto;
124    }
125
126    public HiveJob(JobResult jobResult)
127      : this() {
128      UpdateFromJobResult(jobResult);
129    }
130
131    public HiveJob(OptimizerJob optimizerJob, bool autoCreateChildHiveJobs)
132      : this() {
133      this.syncJobsWithOptimizers = autoCreateChildHiveJobs;
134      this.Job = optimizerJob;
135      this.syncJobsWithOptimizers = true;
136    }
137
138    public HiveJob(IOptimizer optimizer)
139      : this() {
140      this.Job = new OptimizerJob(optimizer);
141    }
142
143    public HiveJob(SerializedJob serializedJob, bool autoCreateChildHiveJobs)
144      : this() {
145      this.syncJobsWithOptimizers = autoCreateChildHiveJobs;
146      this.JobDto = serializedJob.JobInfo;
147      this.Job = SerializedJob.Deserialize<OptimizerJob>(serializedJob.SerializedJobData);
148      this.syncJobsWithOptimizers = true;
149    }
150
151    /// <summary>
152    /// if this.Optimizer is an experiment
153    ///   Uses the child-optimizers of this.HiveJob and creates HiveJob-childs
154    /// if this.Optimizer is a batchrun
155    ///   Creates a number of child-jobs according to repetitions
156    /// </summary>
157    private void UpdateChildHiveJobs() {
158      if (Job != null && syncJobsWithOptimizers) {
159        if (Job.Optimizer is Optimization.Experiment) {
160          Optimization.Experiment experiment = (Optimization.Experiment)Job.Optimizer;
161          foreach (IOptimizer childOpt in experiment.Optimizers) {
162            this.childHiveJobs.Add(new HiveJob(childOpt));
163          }
164        } else if (Job.Optimizer is Optimization.BatchRun) {
165          Optimization.BatchRun batchRun = Job.OptimizerAsBatchRun;
166          if (batchRun.Algorithm != null) {
167            while (this.childHiveJobs.Count < batchRun.Repetitions) {
168              this.childHiveJobs.Add(new HiveJob(batchRun.Algorithm));
169            }
170            while (this.childHiveJobs.Count > batchRun.Repetitions) {
171              this.childHiveJobs.Remove(this.childHiveJobs.Last());
172            }
173          }
174        }
175      }
176    }
177
178    private void RegisterOptimizerEvents() {
179      if (Job != null) {
180        if (Job.Optimizer is Optimization.Experiment) {
181          Optimization.Experiment experiment = Job.OptimizerAsExperiment;
182          experiment.Optimizers.ItemsAdded += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
183          experiment.Optimizers.ItemsReplaced += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
184          experiment.Optimizers.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
185          experiment.Optimizers.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
186        } else if (Job.Optimizer is Optimization.BatchRun) {
187          Optimization.BatchRun batchRun = Job.OptimizerAsBatchRun;
188          batchRun.RepetitionsChanged += new EventHandler(batchRun_RepetitionsChanged);
189          batchRun.AlgorithmChanged += new EventHandler(batchRun_AlgorithmChanged);
190        }
191
192        Job.ComputeInParallelChanged += new EventHandler(Job_ComputeInParallelChanged);
193      }
194    }
195
196    void batchRun_AlgorithmChanged(object sender, EventArgs e) {
197      if (syncJobsWithOptimizers) {
198        this.childHiveJobs.Clear();
199        UpdateChildHiveJobs();
200      }
201    }
202
203    void batchRun_RepetitionsChanged(object sender, EventArgs e) {
204      if (syncJobsWithOptimizers) {
205        UpdateChildHiveJobs();
206      }
207    }
208
209    private void DergisterOptimizerEvents() {
210      if (Job != null && Job.Optimizer is Optimization.Experiment) {
211        if (Job.Optimizer is Optimization.Experiment) {
212          Optimization.Experiment experiment = Job.OptimizerAsExperiment;
213          experiment.Optimizers.ItemsAdded -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
214          experiment.Optimizers.ItemsReplaced -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
215          experiment.Optimizers.ItemsRemoved -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
216          experiment.Optimizers.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
217        } else if (Job.Optimizer is Optimization.BatchRun) {
218          Optimization.BatchRun batchRun = Job.OptimizerAsBatchRun;
219          batchRun.RepetitionsChanged -= new EventHandler(batchRun_RepetitionsChanged);
220          batchRun.AlgorithmChanged -= new EventHandler(batchRun_AlgorithmChanged);
221        }
222
223        Job.ComputeInParallelChanged -= new EventHandler(Job_ComputeInParallelChanged);
224      }
225    }
226
227    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
228      if (syncJobsWithOptimizers && this.Job.ComputeInParallel) {
229        foreach (var item in e.Items) {
230          if (GetChildByOptimizer(item.Value) == null && item.Value.Name != "Placeholder") {
231            this.childHiveJobs.Add(new HiveJob(item.Value));
232          }
233        }
234      }
235    }
236    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
237      if (syncJobsWithOptimizers && this.Job.ComputeInParallel) {
238        foreach (var item in e.OldItems) {
239          this.childHiveJobs.Remove(this.GetChildByOptimizer(item.Value));
240        }
241        foreach (var item in e.Items) {
242          if (GetChildByOptimizer(item.Value) == null && item.Value.Name != "Placeholder") {
243            this.childHiveJobs.Add(new HiveJob(item.Value));
244          }
245        }
246      }
247    }
248    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
249      if (syncJobsWithOptimizers && this.Job.ComputeInParallel) {
250        foreach (var item in e.Items) {
251          this.childHiveJobs.Remove(this.GetChildByOptimizer(item.Value));
252        }
253      }
254    }
255    void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
256      if (syncJobsWithOptimizers && this.Job.ComputeInParallel) {
257        foreach (var item in e.Items) {
258          this.childHiveJobs.Remove(this.GetChildByOptimizer(item.Value));
259        }
260      }
261    }
262
263    void Job_ComputeInParallelChanged(object sender, EventArgs e) {
264      if (Job != null && syncJobsWithOptimizers) {
265        if (Job.ComputeInParallel) {
266          // child-hive jobs are not yet created, so create them according to the child-optimizers
267          this.UpdateChildHiveJobs();
268        } else {
269          // child-hive jobs need to be deleted
270          this.childHiveJobs.Clear();
271        }
272      }
273    }
274
275    public void AddChildHiveJob(HiveJob hiveJob) {
276      this.childHiveJobs.Add(hiveJob);
277      syncJobsWithOptimizers = false;
278      if (this.Job.Optimizer is Optimization.Experiment) {
279        if (!this.Job.OptimizerAsExperiment.Optimizers.Contains(hiveJob.Job.Optimizer)) {
280          UpdateOptimizerInExperiment(this.Job.OptimizerAsExperiment, hiveJob.Job);
281        }
282      } else if (this.Job.Optimizer is Optimization.BatchRun) {
283        UpdateOptimizerInBatchRun(this.Job.OptimizerAsBatchRun, hiveJob.Job);
284      }
285      syncJobsWithOptimizers = true;
286    }
287
288    /// <summary>
289    /// if this.Optimizer is Experiment
290    ///   replace the child-optimizer in the experiment
291    /// if this.Optimizer is BatchRun
292    ///   add the runs from the optimizerJob to the batchrun and replace the algorithm
293    /// </summary>
294    public void UpdateChildOptimizer(OptimizerJob optimizerJob, Guid childJobId) {
295      syncJobsWithOptimizers = false; // don't sync with optimizers during this method
296      ExecutionState executionState = optimizerJob.Optimizer.ExecutionState; // has to be stored before adding the optimizers, because of the sideeffects of updating the optimizers in experiment/batchrun
297
298      if (this.Job.Optimizer is Optimization.Experiment) {
299        UpdateOptimizerInExperiment(this.Job.OptimizerAsExperiment, optimizerJob);
300        executionState = ExecutionState.Stopped;
301      } else if (this.Job.Optimizer is Optimization.BatchRun) {
302        UpdateOptimizerInBatchRun(this.Job.OptimizerAsBatchRun, optimizerJob);
303        executionState = ExecutionState.Stopped;
304      }
305      HiveJob child = this.ChildHiveJobs.Single(j => j.JobDto.Id == childJobId);
306      if (!optimizerJob.ComputeInParallel) {
307        child.syncJobsWithOptimizers = false;
308        child.Job = optimizerJob;
309        child.syncJobsWithOptimizers = true;
310      }
311      if (executionState == ExecutionState.Stopped) {
312        child.IsFinishedOptimizerDownloaded = true;
313      }
314      syncJobsWithOptimizers = true;
315    }
316
317    /// <summary>
318    /// Adds the runs from the optimizerJob to the batchrun and replaces the algorithm
319    /// Sideeffect: the optimizerJob.Optimizer will be prepared (scopes are deleted and executionstate will be reset)
320    /// </summary>
321    private void UpdateOptimizerInBatchRun(BatchRun batchRun, OptimizerJob optimizerJob) {
322      if (batchRun.Algorithm == null) {
323        batchRun.Algorithm = (IAlgorithm)optimizerJob.Optimizer; // only set the first optimizer as algorithm. if every time the Algorithm would be set, the runs would be cleared each time
324      }
325      foreach (IRun run in optimizerJob.Optimizer.Runs) {
326        if (!batchRun.Runs.Contains(run))
327          batchRun.Runs.Add(run);
328      }
329    }
330
331    /// <summary>
332    /// replace the child-optimizer in the experiment
333    /// Sideeffect: the optimizerJob.Optimizer will be prepared (scopes are deleted and executionstate will be reset)
334    /// </summary>
335    private void UpdateOptimizerInExperiment(Optimization.Experiment experiment, OptimizerJob optimizerJob) {
336      if (optimizerJob.IndexInParentOptimizerList < 0)
337        throw new IndexOutOfRangeException("IndexInParentOptimizerList must be equal or greater than zero! The Job is invalid and the optimizer-tree cannot be reassembled.");
338
339      while (experiment.Optimizers.Count < optimizerJob.IndexInParentOptimizerList) {
340        experiment.Optimizers.Add(new UserDefinedAlgorithm("Placeholder")); // add dummy-entries to Optimizers so that its possible to insert the optimizerJob at the correct position
341      }
342      if (experiment.Optimizers.Count < optimizerJob.IndexInParentOptimizerList + 1) {
343        experiment.Optimizers.Add(optimizerJob.Optimizer);
344      } else {
345        // if ComputeInParallel==true, don't replace the optimizer (except it is still a Placeholder)
346        // this is because Jobs with ComputeInParallel get submitted to hive with their child-optimizers deleted
347        if (!optimizerJob.ComputeInParallel || experiment.Optimizers[optimizerJob.IndexInParentOptimizerList].Name == "Placeholder") {
348          experiment.Optimizers[optimizerJob.IndexInParentOptimizerList] = optimizerJob.Optimizer;
349        }
350      }
351    }
352
353    /// <summary>
354    /// Sets the IndexInParentOptimizerList property of the OptimizerJob
355    /// according to the position in the OptimizerList of the parentHiveJob.Job
356    /// Recursively updates all the child-jobs as well
357    /// </summary>
358    internal void SetIndexInParentOptimizerList(HiveJob parentHiveJob) {
359      if (parentHiveJob != null) {
360        if (parentHiveJob.Job.Optimizer is Optimization.Experiment) {
361          this.Job.IndexInParentOptimizerList = parentHiveJob.Job.OptimizerAsExperiment.Optimizers.IndexOf(this.Job.Optimizer);
362        } else if (parentHiveJob.Job.Optimizer is Optimization.BatchRun) {
363          this.Job.IndexInParentOptimizerList = 0;
364        } else {
365          throw new NotSupportedException("Only Experiment and BatchRuns are supported");
366        }
367      }
368      foreach (HiveJob child in childHiveJobs) {
369        child.SetIndexInParentOptimizerList(this);
370      }
371    }
372
373    public override string ToString() {
374      if (jobDto != null) {
375        return job.Name;
376      } else {
377        return base.ToString();
378      }
379    }
380
381    public void UpdateFromJobResult(JobResult jobResult) {
382      if (jobResult != null) {
383        jobDto.Id = jobResult.Id;
384        jobDto.DateCreated = jobResult.DateCreated;
385        jobDto.DateCalculated = jobResult.DateCalculated;
386        jobDto.DateFinished = jobResult.DateFinished;
387        jobDto.Exception = jobResult.Exception;
388        jobDto.Id = jobResult.Id;
389        jobDto.ExecutionTime = jobResult.ExecutionTime;
390        jobDto.State = jobResult.State;
391        // what about parentJob
392        OnJobStateChanged();
393        OnToStringChanged();
394        OnItemImageChanged();
395      }
396    }
397
398    /// <summary>
399    /// Creates a SerializedJob object containing the JobDto and the IJob-Object as byte[]
400    /// </summary>
401    /// <param name="withoutChildOptimizers">
402    ///   if true the Child-Optimizers will not be serialized (if the job contains an Experiment)
403    /// </param>
404    public SerializedJob GetAsSerializedJob(bool withoutChildOptimizers) {
405      byte[] jobByteArray;
406      if (withoutChildOptimizers && this.Job.Optimizer is Optimization.Experiment) {
407        OptimizerJob clonedJob = (OptimizerJob)this.Job.Clone(); // use a cloned job, so that the childHiveJob don't get confused
408        clonedJob.OptimizerAsExperiment.Optimizers.Clear();
409        jobByteArray = SerializedJob.Serialize(clonedJob);
410      } else if (withoutChildOptimizers && this.Job.Optimizer is Optimization.BatchRun) {
411        OptimizerJob clonedJob = (OptimizerJob)this.Job.Clone();
412        clonedJob.OptimizerAsBatchRun.Algorithm = null;
413        jobByteArray = SerializedJob.Serialize(clonedJob);
414      } else if (this.Job.Optimizer is IAlgorithm) {
415        ((IAlgorithm)this.Job.Optimizer).StoreAlgorithmInEachRun = false; // avoid storing the algorithm in runs to reduce size
416        jobByteArray = SerializedJob.Serialize(this.Job);
417      } else {
418        jobByteArray = SerializedJob.Serialize(this.Job);
419      }
420
421      UpdateRequiredPlugins();
422
423      SerializedJob serializedJob = new SerializedJob() {
424        JobInfo = jobDto,
425        SerializedJobData = jobByteArray
426      };
427
428      return serializedJob;
429    }
430
431    /// <summary>
432    /// find out which which plugins are needed for the given object
433    /// </summary>
434    private void UpdateRequiredPlugins() {
435      this.JobDto.PluginsNeeded = HivePluginInfoDto.FindPluginsNeeded(job.GetType());
436    }
437
438    #region Events
439    public event EventHandler JobDtoChanged;
440    private void OnJobDtoChanged() {
441      LogMessage("JobDtoChanged");
442      EventHandler handler = JobDtoChanged;
443      if (handler != null) handler(this, EventArgs.Empty);
444    }
445
446    public event EventHandler JobStateChanged;
447    private void OnJobStateChanged() {
448      LogMessage("JobStateChanged (State: " + this.JobDto.State + ", ExecutionTime: " + this.JobDto.ExecutionTime.ToString() + ")");
449      EventHandler handler = JobStateChanged;
450      if (handler != null) handler(this, EventArgs.Empty);
451    }
452
453    public event EventHandler JobChanged;
454    private void OnJobChanged() {
455      Job_ComputeInParallelChanged(this, EventArgs.Empty);
456      var handler = JobChanged;
457      if (handler != null) handler(this, EventArgs.Empty);
458    }
459
460    public event EventHandler IsFinishedOptimizerDownloadedChanged;
461    private void OnIsFinishedOptimizerDownloadedChanged() {
462      var handler = IsFinishedOptimizerDownloadedChanged;
463      if (handler != null) handler(this, EventArgs.Empty);
464    }
465    #endregion
466
467    public void LogMessage(string message) {
468      lock (locker) {
469        if (job != null) {
470          job.Log.LogMessage(message);
471        }
472      }
473    }
474
475    public override IDeepCloneable Clone(Cloner cloner) {
476      LogMessage("I am beeing cloned");
477      HiveJob clone = (HiveJob)base.Clone(cloner);
478      clone.jobDto = (JobDto)cloner.Clone(this.jobDto);
479      clone.job = (OptimizerJob)cloner.Clone(this.job);
480      return clone;
481    }
482
483    /// <summary>
484    /// Returns a list of HiveJobs including this and all its child-jobs recursively
485    /// </summary>
486    public IEnumerable<HiveJob> GetAllHiveJobs() {
487      List<HiveJob> jobs = new List<HiveJob>();
488      jobs.Add(this);
489      foreach (HiveJob child in this.ChildHiveJobs) {
490        jobs.AddRange(child.GetAllHiveJobs());
491      }
492      return jobs;
493    }
494
495    public HiveJob GetParentByJobId(Guid jobId) {
496      if (this.ChildHiveJobs.SingleOrDefault(j => j.jobDto.Id == jobId) != null)
497        return this;
498      foreach (HiveJob child in this.childHiveJobs) {
499        HiveJob result = child.GetParentByJobId(jobId);
500        if (result != null)
501          return result;
502      }
503      return null;
504    }
505
506
507    public HiveJob GetChildByOptimizerJob(OptimizerJob optimizerJob) {
508      foreach (var child in ChildHiveJobs) {
509        if (child.Job == optimizerJob)
510          return child;
511      }
512      return null;
513    }
514
515
516    public HiveJob GetChildByOptimizer(IOptimizer optimizer) {
517      foreach (var child in ChildHiveJobs) {
518        if (child.Job.Optimizer == optimizer)
519          return child;
520      }
521      return null;
522    }
523
524    /// <summary>
525    /// Searches for an HiveJob object with the correct jobId recursively
526    /// </summary>
527    public HiveJob GetHiveJobByJobId(Guid jobId) {
528      if (this.JobDto.Id == jobId) {
529        return this;
530      } else {
531        foreach (HiveJob child in this.ChildHiveJobs) {
532          HiveJob result = child.GetHiveJobByJobId(jobId);
533          if (result != null)
534            return result;
535        }
536      }
537      return null;
538    }
539
540
541    public void RemoveByJobId(Guid jobId) {
542      IEnumerable<HiveJob> jobs = ChildHiveJobs.Where(j => j.JobDto.Id == jobId).ToList(); // if Guid.Empty needs to be removed, there could be more than one with this jobId
543      foreach (HiveJob j in jobs) {
544        this.childHiveJobs.Remove(j);
545      }
546      foreach (HiveJob child in ChildHiveJobs) {
547        child.RemoveByJobId(jobId);
548      }
549    }
550
551  }
552}
Note: See TracBrowser for help on using the repository browser.