#region License Information /* HeuristicLab * Copyright (C) 2002-2011 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 . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Threading; using HeuristicLab.Collections; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.PluginInfrastructure; namespace HeuristicLab.Clients.Hive { [Item("Hive Job", "Represents a hive job.")] [StorableClass] public class HiveJob : NamedItem, IItemTree { protected static object locker = new object(); protected ReaderWriterLockSlim childHiveJobsLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); public override Image ItemImage { get { if (job.Id == Guid.Empty) { // not yet uploaded return HeuristicLab.Common.Resources.VSImageLibrary.Event; } else { if (job.State == JobState.Waiting) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutablePrepared; else if (job.State == JobState.Calculating) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStarted; else if (job.State == JobState.Transferring) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStarted; else if (job.State == JobState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutablePaused; else if (job.State == JobState.Aborted) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStopped; else if (job.State == JobState.Failed) return HeuristicLab.Common.Resources.VSImageLibrary.Error; else if (job.State == JobState.Finished) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStopped; else return HeuristicLab.Common.Resources.VSImageLibrary.Event; } } } [Storable] protected Job job; public Job Job { get { return job; } set { if (job != value) { DeregisterJobEvents(); job = value; RegisterJobEvents(); IsFinishedJobDownloaded = false; OnJobChanged(); OnToStringChanged(); OnItemImageChanged(); } } } [Storable] protected ItemJob itemJob; public ItemJob ItemJob { get { return itemJob; } set { if (itemJob != null && syncJobsWithOptimizers) { this.childHiveJobs.Clear(); } if (itemJob != value) { DergisterItemJobEvents(); itemJob = value; RegisterItemJobEvents(); OnItemJobChanged(); IsFinishedJobDownloaded = true; } } } // job downloaded since last status change [Storable] private bool isFinishedJobDownloaded = false; public bool IsFinishedJobDownloaded { get { return isFinishedJobDownloaded; } set { if (value != isFinishedJobDownloaded) { this.isFinishedJobDownloaded = value; OnIsFinishedJobDownloadedChanged(); } } } public bool IsDownloading { get; set; } [Storable] protected ItemList childHiveJobs; public virtual ReadOnlyItemList ChildHiveJobs { get { childHiveJobsLock.EnterReadLock(); try { return childHiveJobs.AsReadOnly(); } finally { childHiveJobsLock.ExitReadLock(); } } } [Storable] protected bool syncJobsWithOptimizers = true; public StateLogList StateLog { get { return new StateLogList(this.job.StateLog); } } public StateLogListList ChildStateLogList { get { return new StateLogListList(this.childHiveJobs.Select(x => x.StateLog)); } } #region Constructors and Cloning public HiveJob() { this.Job = new Job() { CoresNeeded = 1, MemoryNeeded = 0 }; job.State = JobState.Offline; this.childHiveJobs = new ItemList(); syncJobsWithOptimizers = true; RegisterChildHiveJobEvents(); } public HiveJob(ItemJob itemJob, bool autoCreateChildHiveJobs) : this() { this.syncJobsWithOptimizers = autoCreateChildHiveJobs; this.ItemJob = itemJob; this.syncJobsWithOptimizers = true; } public HiveJob(Job job, JobData jobData, bool autoCreateChildHiveJobs) { this.syncJobsWithOptimizers = autoCreateChildHiveJobs; this.Job = job; try { this.ItemJob = PersistenceUtil.Deserialize(jobData.Data); } catch { this.ItemJob = null; } this.childHiveJobs = new ItemList(); this.syncJobsWithOptimizers = true; RegisterChildHiveJobEvents(); } protected HiveJob(HiveJob original, Cloner cloner) : base(original, cloner) { this.Job = cloner.Clone(original.job); this.ItemJob = cloner.Clone(original.ItemJob); original.childHiveJobsLock.EnterReadLock(); try { this.childHiveJobs = cloner.Clone(original.childHiveJobs); } finally { original.childHiveJobsLock.ExitReadLock(); } this.syncJobsWithOptimizers = original.syncJobsWithOptimizers; this.isFinishedJobDownloaded = original.isFinishedJobDownloaded; } public override IDeepCloneable Clone(Cloner cloner) { return new HiveJob(this, cloner); } #endregion protected virtual void UpdateChildHiveJobs() { } protected virtual void RegisterItemJobEvents() { if (ItemJob != null) { ItemJob.ComputeInParallelChanged += new EventHandler(ItemJob_ComputeInParallelChanged); ItemJob.ToStringChanged += new EventHandler(ItemJob_ToStringChanged); } } protected virtual void DergisterItemJobEvents() { if (ItemJob != null) { ItemJob.ComputeInParallelChanged -= new EventHandler(ItemJob_ComputeInParallelChanged); ItemJob.ToStringChanged -= new EventHandler(ItemJob_ToStringChanged); } } protected virtual void RegisterChildHiveJobEvents() { this.childHiveJobs.ItemsAdded += new CollectionItemsChangedEventHandler>(OnItemsAdded); this.childHiveJobs.ItemsRemoved += new CollectionItemsChangedEventHandler>(OnItemsRemoved); this.childHiveJobs.CollectionReset += new CollectionItemsChangedEventHandler>(OnCollectionReset); } protected virtual void DeregisterChildHiveJobEvents() { this.childHiveJobs.ItemsAdded -= new CollectionItemsChangedEventHandler>(OnItemsAdded); this.childHiveJobs.ItemsRemoved -= new CollectionItemsChangedEventHandler>(OnItemsRemoved); this.childHiveJobs.CollectionReset -= new CollectionItemsChangedEventHandler>(OnCollectionReset); } protected virtual void ItemJob_ToStringChanged(object sender, EventArgs e) { this.OnToStringChanged(); } protected virtual void ItemJob_ComputeInParallelChanged(object sender, EventArgs e) { if (ItemJob != null && syncJobsWithOptimizers) { this.UpdateChildHiveJobs(); } } public virtual void AddChildHiveJob(HiveJob hiveJob) { childHiveJobsLock.EnterWriteLock(); try { this.childHiveJobs.Add(hiveJob); } finally { childHiveJobsLock.ExitWriteLock(); } } public override string ToString() { if (itemJob != null && itemJob.Item != null) { return itemJob.ToString(); } else { return Job.Id.ToString(); } } public virtual void UpdateFromLightweightJob(LightweightJob lightweightJob) { if (lightweightJob != null) { job.Id = lightweightJob.Id; job.ParentJobId = lightweightJob.ParentJobId; job.ExecutionTime = lightweightJob.ExecutionTime; job.State = lightweightJob.State; job.StateLog = new List(lightweightJob.StateLog); job.Command = lightweightJob.Command; OnJobStateChanged(); OnToStringChanged(); OnItemImageChanged(); OnStateLogChanged(); } } /// /// Creates a JobData object containing the Job and the IJob-Object as byte[] /// /// /// if true the Child-Optimizers will not be serialized (if the job contains an Experiment) /// public virtual JobData GetAsJobData(bool withoutChildOptimizers, out List plugins) { plugins = new List(); if (this.itemJob == null) return null; IEnumerable usedTypes; byte[] jobByteArray = PersistenceUtil.Serialize(this.ItemJob, out usedTypes); JobData jobData = new JobData() { JobId = job.Id, Data = jobByteArray }; PluginUtil.CollectDeclaringPlugins(plugins, usedTypes); return jobData; } #region Event Handler public event EventHandler JobChanged; private void OnJobChanged() { EventHandler handler = JobChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler JobStateChanged; private void OnJobStateChanged() { EventHandler handler = JobStateChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ItemJobChanged; private void OnItemJobChanged() { ItemJob_ComputeInParallelChanged(this, EventArgs.Empty); var handler = ItemJobChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler IsFinishedJobDownloadedChanged; private void OnIsFinishedJobDownloadedChanged() { var handler = IsFinishedJobDownloadedChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler StateLogChanged; private void OnStateLogChanged() { var handler = StateLogChanged; if (handler != null) handler(this, EventArgs.Empty); } private void RegisterJobEvents() { if (job != null) job.PropertyChanged += new PropertyChangedEventHandler(job_PropertyChanged); } private void DeregisterJobEvents() { if (job != null) job.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(job_PropertyChanged); } private void job_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "State") { IsFinishedJobDownloaded = false; } } #endregion /// /// Returns a list of HiveJobs including this and all its child-jobs recursively /// public IEnumerable GetAllHiveJobs() { childHiveJobsLock.EnterReadLock(); try { var jobs = new List(); jobs.Add(this); foreach (HiveJob child in this.childHiveJobs) { jobs.AddRange(child.GetAllHiveJobs()); } return jobs; } finally { childHiveJobsLock.ExitReadLock(); } } public HiveJob GetParentByJobId(Guid jobId) { childHiveJobsLock.EnterReadLock(); try { if (this.ChildHiveJobs.SingleOrDefault(j => j.job.Id == jobId) != null) return this; foreach (HiveJob child in this.childHiveJobs) { HiveJob result = child.GetParentByJobId(jobId); if (result != null) return result; } return null; } finally { childHiveJobsLock.ExitWriteLock(); } } /// /// Searches for an HiveJob object with the correct jobId recursively /// public HiveJob GetHiveJobByJobId(Guid jobId) { if (this.Job.Id == jobId) { return this; } else { childHiveJobsLock.EnterReadLock(); try { foreach (HiveJob child in this.childHiveJobs) { HiveJob result = child.GetHiveJobByJobId(jobId); if (result != null) return result; } } finally { childHiveJobsLock.ExitReadLock(); } } return null; } public void RemoveByJobId(Guid jobId) { childHiveJobsLock.EnterWriteLock(); try { IEnumerable jobs = childHiveJobs.Where(j => j.Job.Id == jobId).ToList(); foreach (HiveJob j in jobs) { this.childHiveJobs.Remove(j); } foreach (HiveJob child in childHiveJobs) { child.RemoveByJobId(jobId); } } finally { childHiveJobsLock.ExitWriteLock(); } } public IEnumerable> GetChildItems() { return this.ChildHiveJobs; } #region INotifyObservableCollectionItemsChanged Members public event CollectionItemsChangedEventHandler> CollectionReset; private void OnCollectionReset(object sender, CollectionItemsChangedEventArgs> e) { foreach (var item in e.Items) { item.Value.StateLogChanged -= new EventHandler(ChildHiveJob_StateLogChanged); } var handler = CollectionReset; if (handler != null) handler(this, ToCollectionItemsChangedEventArgs(e)); } public event CollectionItemsChangedEventHandler> ItemsAdded; private void OnItemsAdded(object sender, CollectionItemsChangedEventArgs> e) { foreach (var item in e.Items) { item.Value.StateLogChanged += new EventHandler(ChildHiveJob_StateLogChanged); } var handler = ItemsAdded; if (handler != null) handler(this, ToCollectionItemsChangedEventArgs(e)); } public event CollectionItemsChangedEventHandler> ItemsRemoved; private void OnItemsRemoved(object sender, CollectionItemsChangedEventArgs> e) { foreach (var item in e.Items) { item.Value.StateLogChanged -= new EventHandler(ChildHiveJob_StateLogChanged); } var handler = ItemsRemoved; if (handler != null) handler(this, ToCollectionItemsChangedEventArgs(e)); } private static CollectionItemsChangedEventArgs> ToCollectionItemsChangedEventArgs(CollectionItemsChangedEventArgs> e) { return new CollectionItemsChangedEventArgs>(e.Items.Select(x => x.Value), e.OldItems == null ? null : e.OldItems.Select(x => x.Value)); } private void ChildHiveJob_StateLogChanged(object sender, EventArgs e) { OnStateLogChanged(); } #endregion public void Pause() { if (this.Job.IsParentJob) { childHiveJobsLock.EnterReadLock(); try { foreach (var child in childHiveJobs) { ServiceLocator.Instance.CallHiveService(s => s.PauseJob(child.job.Id)); } } finally { childHiveJobsLock.ExitReadLock(); } } else { ServiceLocator.Instance.CallHiveService(s => s.PauseJob(this.job.Id)); } } public void Stop() { if (this.Job.IsParentJob) { childHiveJobsLock.EnterReadLock(); try { foreach (var child in childHiveJobs) { ServiceLocator.Instance.CallHiveService(s => s.StopJob(child.job.Id)); } } finally { childHiveJobsLock.ExitReadLock(); } } else { ServiceLocator.Instance.CallHiveService(s => s.StopJob(this.job.Id)); } } public void Restart() { ServiceLocator.Instance.CallHiveService(service => { JobData jobData = new JobData(); jobData.JobId = this.job.Id; jobData.Data = PersistenceUtil.Serialize(this.itemJob); service.UpdateJobData(this.Job, jobData); service.RestartJob(this.job.Id); Job job = service.GetJob(this.job.Id); this.job.LastJobDataUpdate = job.LastJobDataUpdate; }); } public ICollection> Actions { get { return new List>(); } } public virtual void IntegrateChild(ItemJob job, Guid childJobId) { } /// /// Delete ItemJob /// public virtual void ClearData() { this.ItemJob.Item = null; } } [Item("Hive Job", "Represents a hive job.")] [StorableClass] public class HiveJob : HiveJob where T : ItemJob { public new T ItemJob { get { return (T)base.ItemJob; } internal set { base.ItemJob = value; } } #region Constructors and Cloning public HiveJob() : base() { } public HiveJob(T itemJob) : base(itemJob, true) { } protected HiveJob(HiveJob original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new HiveJob(this, cloner); } #endregion } }