Index: branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive/3.4/ExperimentManager/HiveExperimentClient.cs
===================================================================
--- branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive/3.4/ExperimentManager/HiveExperimentClient.cs	(revision 5402)
+++ branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive/3.4/ExperimentManager/HiveExperimentClient.cs	(revision 5404)
@@ -114,8 +114,14 @@
     }
 
-    private List<Guid> pluginsNeededIds;
-    public List<Guid> PluginsNeededIds {
-      get { return pluginsNeededIds; }
-      set { pluginsNeededIds = value; } 
+    private IEnumerable<Plugin> onlinePlugins;
+    public IEnumerable<Plugin> OnlinePlugins {
+      get { return onlinePlugins; }
+      set { onlinePlugins = value; } 
+    }
+
+    private List<Plugin> alreadyUploadedPlugins;
+    public List<Plugin> AlreadyUploadedPlugins {
+      get { return alreadyUploadedPlugins; }
+      set { alreadyUploadedPlugins = value; }
     }
 
@@ -247,5 +253,6 @@
 
           this.progress.Status = "Uploading plugins...";
-          this.PluginsNeededIds = GetPluginsNeededIds(this.useLocalPlugins);
+          this.OnlinePlugins = service.Obj.GetPlugins();
+          this.AlreadyUploadedPlugins = new List<Plugin>();
 
           this.progress.Status = "Uploading jobs...";
@@ -269,40 +276,4 @@
         IsProgressing = false;
       }
-    }
-
-    /// <summary>
-    /// Gets the Ids of all plugins needed for executing the job.
-    /// All loaded plugins are assumed to be necessary.
-    /// If a plugin with the same name and version is already online, it is used. Otherwise the local plugin is uploaded.
-    /// If useLocalPlugins is true, all local plugins are uploaded regardless of the existence of the same plugin online.
-    /// </summary>
-    public static List<Guid> GetPluginsNeededIds(bool useLocalPlugins) {
-      IEnumerable<IPluginDescription> localPlugins = ApplicationManager.Manager.Plugins;
-      List<Guid> pluginsNeededIds = new List<Guid>();
-
-      using (var service = ServiceLocator.Instance.GetService()) {
-        IEnumerable<Plugin> onlinePlugins = service.Obj.GetPlugins();
-
-        foreach (IPluginDescription localPlugin in localPlugins) {
-          Plugin found = onlinePlugins.Where(onlinePlugin => onlinePlugin.Name == localPlugin.Name && onlinePlugin.Version == localPlugin.Version).SingleOrDefault();
-          if (!useLocalPlugins && found != null) {
-            // plugin is available online; reuse
-            pluginsNeededIds.Add(found.Id);
-          } else {
-            // upload the plugin
-            Plugin p = new Plugin() { Name = localPlugin.Name, Version = localPlugin.Version, IsLocal = useLocalPlugins };
-            List<PluginData> pluginDatas = new List<PluginData>();
-
-            foreach (IPluginFile pf in localPlugin.Files) {
-              PluginData pluginData = new PluginData();
-
-              pluginData.Data = File.ReadAllBytes(pf.Name);
-              pluginDatas.Add(pluginData);
-            }
-            pluginsNeededIds.Add(service.Obj.AddPlugin(p, pluginDatas));
-          }
-        }
-      }
-      return pluginsNeededIds;
     }
 
@@ -319,14 +290,15 @@
       this.progress.Status = string.Format("Serializing job {0} of {1}", jobCount, totalJobCount);
       JobData jobData;
+      List<IPluginDescription> plugins;
       if (hiveJob.OptimizerJob.ComputeInParallel &&
         (hiveJob.OptimizerJob.Optimizer is Optimization.Experiment || hiveJob.OptimizerJob.Optimizer is Optimization.BatchRun)) {
         hiveJob.Job.JobState = JobState.WaitingForChildJobs;
         hiveJob.OptimizerJob.CollectChildJobs = false; // don't collect child-jobs on slaves
-        jobData = hiveJob.GetAsJobData(true);
+        jobData = hiveJob.GetAsJobData(true, out plugins);
       } else {
-        jobData = hiveJob.GetAsJobData(false);
-      }
-
-      hiveJob.Job.PluginsNeededIds = this.PluginsNeededIds;
+        jobData = hiveJob.GetAsJobData(false, out plugins);
+      }
+
+      hiveJob.Job.PluginsNeededIds = GetPluginDependencies(service, onlinePlugins, alreadyUploadedPlugins, plugins, useLocalPlugins);
 
       this.progress.Status = string.Format("Uploading job {0} of {1} ({2} kb)", jobCount, totalJobCount, jobData.Data.Count() / 1024);
@@ -347,5 +319,5 @@
       }
     }
-
+    
     /// <summary>
     /// Converts a string which can contain Ids separated by ';' to a enumerable
@@ -369,19 +341,4 @@
     #endregion
 
-    public void StartResultPolling() {
-      if (!jobResultPoller.IsPolling) {
-        jobResultPoller.Start();
-      } else {
-        throw new JobResultPollingException("Result polling already running");
-      }
-    }
-
-    public void StopResultPolling() {
-      if (jobResultPoller.IsPolling) {
-        jobResultPoller.Stop();
-      } else {
-        throw new JobResultPollingException("Result polling not running");
-      }
-    }
 
     #region HiveJob Events
@@ -483,4 +440,21 @@
 
     #region JobResultPoller Events
+
+    public void StartResultPolling() {
+      if (!jobResultPoller.IsPolling) {
+        jobResultPoller.Start();
+      } else {
+        throw new JobResultPollingException("Result polling already running");
+      }
+    }
+
+    public void StopResultPolling() {
+      if (jobResultPoller.IsPolling) {
+        jobResultPoller.Stop();
+      } else {
+        throw new JobResultPollingException("Result polling not running");
+      }
+    }
+
     private void RegisterResultPollingEvents() {
       jobResultPoller.ExceptionOccured += new EventHandler<EventArgs<Exception>>(jobResultPoller_ExceptionOccured);
@@ -497,14 +471,14 @@
       jobResultPoller.IsPollingChanged -= new EventHandler(jobResultPoller_IsPollingChanged);
     }
-    void jobResultPoller_IsPollingChanged(object sender, EventArgs e) {
+    private void jobResultPoller_IsPollingChanged(object sender, EventArgs e) {
       this.IsPollingResults = jobResultPoller.IsPolling;
     }
-    void jobResultPoller_PollingFinished(object sender, EventArgs e) {
+    private void jobResultPoller_PollingFinished(object sender, EventArgs e) {
       LogMessage("Polling results finished");
     }
-    void jobResultPoller_PollingStarted(object sender, EventArgs e) {
+    private void jobResultPoller_PollingStarted(object sender, EventArgs e) {
       LogMessage("Polling results started");
     }
-    void jobResultPoller_JobResultReceived(object sender, EventArgs<IEnumerable<LightweightJob>> e) {
+    private void jobResultPoller_JobResultReceived(object sender, EventArgs<IEnumerable<LightweightJob>> e) {
       foreach (LightweightJob lightweightJob in e.Value) {
         HiveJob hj = hiveJob.GetHiveJobByJobId(lightweightJob.Id);
@@ -543,5 +517,5 @@
     }
 
-    void jobResultPoller_ExceptionOccured(object sender, EventArgs<Exception> e) {
+    private void jobResultPoller_ExceptionOccured(object sender, EventArgs<Exception> e) {
       OnExceptionOccured(e.Value);
     }
@@ -578,4 +552,5 @@
     #endregion
 
+    #region Job Loading
     /// <summary>
     /// Downloads the root job from hive and sets the experiment, rootJob and rootJobItem
@@ -674,5 +649,90 @@
       }
     }
-    
+    #endregion 
+
+    #region Plugin Management
+    /// <summary>
+    /// Checks if plugins are available on Hive Server. If not they are uploaded. Ids are returned.
+    /// </summary>
+    /// <param name="service">An active service-proxy</param>
+    /// <param name="onlinePlugins">List of plugins which are available online</param>
+    /// <param name="alreadyUploadedPlugins">List of plugins which have been uploaded from this HiveExperiment</param>
+    /// <param name="neededPlugins">List of plugins which need to be uploaded</param>
+    /// <param name="useLocalPlugins">If true, the plugins which are already online are ignored. All local plugins are uploaded, but only once.</param>
+    /// <returns></returns>
+    private static List<Guid> GetPluginDependencies(IHiveService service, IEnumerable<Plugin> onlinePlugins, List<Plugin> alreadyUploadedPlugins, IEnumerable<IPluginDescription> neededPlugins, bool useLocalPlugins) {
+      var pluginIds = new List<Guid>();
+      foreach (var neededPlugin in neededPlugins) {
+        Plugin foundPlugin = alreadyUploadedPlugins.SingleOrDefault(p => p.Name == neededPlugin.Name && p.Version == neededPlugin.Version);
+        if (foundPlugin == null) {
+          foundPlugin = onlinePlugins.SingleOrDefault(p => p.Name == neededPlugin.Name && p.Version == neededPlugin.Version);
+          if (useLocalPlugins || foundPlugin == null) {
+            Plugin p = CreatePlugin(neededPlugin, useLocalPlugins);
+            List<PluginData> pd = CreatePluginDatas(neededPlugin);
+            p.Id = service.AddPlugin(p, pd);
+            alreadyUploadedPlugins.Add(p);
+          } else {
+            pluginIds.Add(foundPlugin.Id);
+          }
+        } else {
+          pluginIds.Add(foundPlugin.Id);
+        }
+      }
+      return pluginIds;
+    }
+
+    private static Plugin CreatePlugin(IPluginDescription plugin, bool useLocalPlugins) {
+      return new Plugin() { Name = plugin.Name, Version = plugin.Version, IsLocal = useLocalPlugins };
+    }
+
+    private static List<PluginData> CreatePluginDatas(IPluginDescription plugin) {
+      List<PluginData> pluginDatas = new List<PluginData>();
+
+      foreach (IPluginFile pf in plugin.Files) {
+        PluginData pluginData = new PluginData();
+
+        pluginData.Data = File.ReadAllBytes(pf.Name);
+        pluginData.FileName = Path.GetFileName(pf.Name);
+        pluginDatas.Add(pluginData);
+      }
+      return pluginDatas;
+    }
+
+    /// <summary>
+    /// Gets the Ids of all plugins needed for executing the job.
+    /// All loaded plugins are assumed to be necessary.
+    /// If a plugin with the same name and version is already online, it is used. Otherwise the local plugin is uploaded.
+    /// If useLocalPlugins is true, all local plugins are uploaded regardless of the existence of the same plugin online.
+    /// </summary>
+    //public static List<Guid> GetPluginsNeededIds(bool useLocalPlugins) {
+    //  IEnumerable<IPluginDescription> localPlugins = ApplicationManager.Manager.Plugins;
+    //  List<Guid> pluginsNeededIds = new List<Guid>();
+
+    //  using (var service = ServiceLocator.Instance.GetService()) {
+    //    IEnumerable<Plugin> onlinePlugins = service.Obj.GetPlugins();
+
+    //    foreach (IPluginDescription localPlugin in localPlugins) {
+    //      Plugin found = onlinePlugins.Where(onlinePlugin => onlinePlugin.Name == localPlugin.Name && onlinePlugin.Version == localPlugin.Version).SingleOrDefault();
+    //      if (!useLocalPlugins && found != null) {
+    //        // plugin is available online; reuse
+    //        pluginsNeededIds.Add(found.Id);
+    //      } else {
+    //        // upload the plugin
+    //        Plugin p = new Plugin() { Name = localPlugin.Name, Version = localPlugin.Version, IsLocal = useLocalPlugins };
+    //        List<PluginData> pluginDatas = new List<PluginData>();
+
+    //        foreach (IPluginFile pf in localPlugin.Files) {
+    //          PluginData pluginData = new PluginData();
+
+    //          pluginData.Data = File.ReadAllBytes(pf.Name);
+    //          pluginDatas.Add(pluginData);
+    //        }
+    //        pluginsNeededIds.Add(service.Obj.AddPlugin(p, pluginDatas));
+    //      }
+    //    }
+    //  }
+    //  return pluginsNeededIds;
+    //}
+    #endregion
   }
 }
Index: branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive/3.4/ExperimentManager/HiveJobClient.cs
===================================================================
--- branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive/3.4/ExperimentManager/HiveJobClient.cs	(revision 5402)
+++ branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive/3.4/ExperimentManager/HiveJobClient.cs	(revision 5404)
@@ -32,4 +32,5 @@
 using HeuristicLab.PluginInfrastructure;
 using HeuristicLab.Services.Hive.Common.DataTransfer;
+using HeuristicLab.PluginInfrastructure.Manager;
 
 namespace HeuristicLab.Clients.Hive {
@@ -425,22 +426,24 @@
     ///   if true the Child-Optimizers will not be serialized (if the job contains an Experiment)
     /// </param>
-    public JobData GetAsJobData(bool withoutChildOptimizers) {
+    public JobData GetAsJobData(bool withoutChildOptimizers, out List<IPluginDescription> plugins) {
+      plugins = new List<IPluginDescription>();
       if (this.optimizerJob == null || this.optimizerJob.Optimizer == null)
         return null;
 
+      IEnumerable<Type> usedTypes;
       byte[] jobByteArray;
       if (withoutChildOptimizers && this.OptimizerJob.Optimizer is Optimization.Experiment) {
         OptimizerJob clonedJob = (OptimizerJob)this.OptimizerJob.Clone(); // use a cloned job, so that the childHiveJob don't get confused
         clonedJob.OptimizerAsExperiment.Optimizers.Clear();
-        jobByteArray = PersistenceUtil.Serialize(clonedJob);
+        jobByteArray = PersistenceUtil.Serialize(clonedJob, out usedTypes);
       } else if (withoutChildOptimizers && this.OptimizerJob.Optimizer is Optimization.BatchRun) {
         OptimizerJob clonedJob = (OptimizerJob)this.OptimizerJob.Clone();
         clonedJob.OptimizerAsBatchRun.Optimizer = null;
-        jobByteArray = PersistenceUtil.Serialize(clonedJob);
+        jobByteArray = PersistenceUtil.Serialize(clonedJob, out usedTypes);
       } else if (this.OptimizerJob.Optimizer is IAlgorithm) {
         ((IAlgorithm)this.OptimizerJob.Optimizer).StoreAlgorithmInEachRun = false; // avoid storing the algorithm in runs to reduce size
-        jobByteArray = PersistenceUtil.Serialize(this.OptimizerJob);
+        jobByteArray = PersistenceUtil.Serialize(this.OptimizerJob, out usedTypes);
       } else {
-        jobByteArray = PersistenceUtil.Serialize(this.OptimizerJob);
+        jobByteArray = PersistenceUtil.Serialize(this.OptimizerJob, out usedTypes);
       }
 
@@ -450,5 +453,27 @@
       };
 
+      CollectDeclaringPlugins(plugins, usedTypes);
+
       return jobData;
+    }
+
+    private void CollectDeclaringPlugins(List<IPluginDescription> plugins, IEnumerable<Type> usedTypes) {
+      foreach (Type type in usedTypes) {
+        var plugin = ApplicationManager.Manager.GetDeclaringPlugin(type);
+        if (plugin != null && !plugins.Contains(plugin)) {
+          plugins.Add(plugin);
+          CollectPluginDependencies(plugins, plugin);
+        }
+      }
+    }
+
+    private void CollectPluginDependencies(List<IPluginDescription> plugins, IPluginDescription plugin) {
+      if (plugin == null) return;
+      foreach (var dependency in plugin.Dependencies) {
+        if (!plugins.Contains(dependency)) {
+          plugins.Add(dependency);
+          CollectPluginDependencies(plugins, dependency);
+        }
+      }
     }
 
