source: trunk/sources/HeuristicLab.Grid/GridClient.cs @ 766

Last change on this file since 766 was 766, checked in by gkronber, 13 years ago

fixed #362 (GridClient doesn't work since the change to use major.minor version-numbers for plugins)

File size: 7.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 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
21using System;
22using System.Collections.Generic;
23using System.Linq;
24using System.Text;
25using System.ServiceModel;
26using System.Diagnostics;
27using System.Threading;
28using HeuristicLab.PluginInfrastructure;
29
30namespace HeuristicLab.Grid {
31  class GridClient {
32    private string uri;
33    private ChannelFactory<IEngineStore> factory;
34    private System.Timers.Timer fetchOperationTimer;
35    private IEngineStore engineStore;
36    private object connectionLock = new object();
37    private const int CONNECTION_RETRY_TIMEOUT_SEC = 10;
38    private const int MAX_RETRIES = 10;
39
40    public bool Waiting {
41      get {
42        return !executing && !stopped;
43      }
44    }
45
46    private bool executing;
47    public bool Executing {
48      get {
49        return executing;
50      }
51    }
52
53    private bool stopped;
54    public bool Stopped {
55      get {
56        return stopped;
57      }
58    }
59
60    private string statusMessage = "";
61    public string StatusMessage {
62      get {
63        return statusMessage;
64      }
65    }
66
67    internal GridClient() {
68      fetchOperationTimer = new System.Timers.Timer();
69      fetchOperationTimer.Interval = 200;
70      fetchOperationTimer.Elapsed += new System.Timers.ElapsedEventHandler(fetchOperationTimer_Elapsed);
71      stopped = true;
72    }
73
74    internal void Start(string uri) {
75      try {
76        this.uri = uri;
77        ResetConnection();
78        fetchOperationTimer.Start();
79        stopped = false;
80      } catch(CommunicationException ex) {
81        statusMessage = DateTime.Now.ToShortTimeString()+": Exception while connecting to the server: " + ex.Message;
82        fetchOperationTimer.Stop();
83      }
84    }
85
86    internal void Stop() {
87      fetchOperationTimer.Stop();
88      lock(connectionLock) {
89        if(factory.State == CommunicationState.Opened || factory.State == CommunicationState.Opening) {
90          IAsyncResult closeResult = factory.BeginClose(null, null);
91          factory.EndClose(closeResult);
92        }
93      }
94      stopped = true;
95    }
96
97    private void ResetConnection() {
98      Trace.TraceInformation("Reset connection in GridClient");
99      NetTcpBinding binding = new NetTcpBinding();
100      binding.MaxReceivedMessageSize = 100000000; // 100Mbytes
101      binding.ReaderQuotas.MaxStringContentLength = 100000000; // also 100M chars
102      binding.ReaderQuotas.MaxArrayLength = 100000000; // also 100M elements;
103      binding.Security.Mode = SecurityMode.None;
104      factory = new ChannelFactory<IEngineStore>(binding);
105      engineStore = factory.CreateChannel(new EndpointAddress(uri));
106    }
107
108    private void fetchOperationTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
109      try {
110        byte[] engineXml = null;
111        Guid currentGuid;
112        // first stop the timer!
113        fetchOperationTimer.Stop();
114        bool gotEngine = false;
115        lock(connectionLock) {
116          if(stopped) return;
117          try {
118            gotEngine = engineStore.TryTakeEngine(out currentGuid, out engineXml);
119          } catch(TimeoutException) {
120            ChangeStatusMessage("TimeoutException while trying to get an engine");
121            currentGuid = Guid.Empty;
122            // timeout -> just start the timer again
123            fetchOperationTimer.Interval = 5000;
124            fetchOperationTimer.Start();
125          } catch(CommunicationException) {
126            ChangeStatusMessage("CommunicationException while trying to get an engine");
127            // connection problem -> reset connection and start the timer again
128            ResetConnection();
129            currentGuid = Guid.Empty;
130            fetchOperationTimer.Interval = 5000;
131            fetchOperationTimer.Start();
132          }
133        }
134        // got engine from server and user didn't press stop -> execute the engine
135        if(gotEngine && !stopped) {
136          executing = true;
137          AppDomain engineDomain = PluginManager.Manager.CreateAndInitAppDomain("Engine domain");
138          Type engineRunnerType = typeof(EngineRunner);
139         
140          EngineRunner runner = (EngineRunner)engineDomain.CreateInstanceAndUnwrap(engineRunnerType.Assembly.GetName().Name, engineRunnerType.FullName);
141          byte[] resultXml = runner.Execute(engineXml);
142          bool success = false;
143          int retries = 0;
144          do {
145            lock(connectionLock) {
146              if(!stopped) {
147                try {
148                  engineStore.StoreResult(currentGuid, resultXml);
149                  success = true;
150                } catch(TimeoutException) {
151                  ChangeStatusMessage("TimeoutException while trying to store the result of an engine");
152                  success = false;
153                  retries++;
154                  Thread.Sleep(TimeSpan.FromSeconds(CONNECTION_RETRY_TIMEOUT_SEC));
155                } catch(CommunicationException) {
156                  ChangeStatusMessage("CommunicationException while trying to store the result of an engine");
157                  ResetConnection();
158                  success = false;
159                  retries++;
160                  Thread.Sleep(TimeSpan.FromSeconds(CONNECTION_RETRY_TIMEOUT_SEC));
161                }
162              }
163            }
164          } while(!stopped && !success && retries < MAX_RETRIES);
165          // dispose the AppDomain that was created to run the job
166          AppDomain.Unload(engineDomain);
167          executing = false;
168          // ok if we could store the result it's probable that the server can send us another engine use a small time-interval
169          if(success)
170            fetchOperationTimer.Interval = 100;
171          else fetchOperationTimer.Interval = 30000; // if there were problems -> sleep for a longer time
172          // clear state
173          currentGuid = Guid.Empty;
174          // start the timer
175          fetchOperationTimer.Start();
176        } else {
177          // ok we didn't get engine -> if the user didn't press stop this means that the server doesn't have engines for us
178          // if the user pressed stop we must not start the timer
179          if(!stopped) {
180            // start the timer again
181            fetchOperationTimer.Interval = 5000;
182            fetchOperationTimer.Start();
183          }
184        }
185      } catch(Exception ex) {
186        // in case something goes wrong when creating / unloading the AppDomain
187        ChangeStatusMessage("Uncaught exception " + ex.Message);
188        Stop();
189      }
190    }
191
192    private void ChangeStatusMessage(string msg) {
193      Trace.TraceWarning(msg);
194      statusMessage = DateTime.Now.ToShortTimeString() + ": " + msg;
195    }
196  }
197}
Note: See TracBrowser for help on using the repository browser.