Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Hive.Server.Core/ClientCommunicator.cs @ 1332

Last change on this file since 1332 was 1272, checked in by msteinbi, 16 years ago

Main structure of Sheduler (including Interface and first minimalistic implementation) (#507)

File size: 13.5 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
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26using HeuristicLab.Hive.Contracts.BusinessObjects;
27using HeuristicLab.Hive.Contracts.Interfaces;
28using HeuristicLab.Hive.Contracts;
29using HeuristicLab.Core;
30using HeuristicLab.Hive.Server.Core.InternalInterfaces.DataAccess;
31using System.Resources;
32using System.Reflection;
33using HeuristicLab.Hive.JobBase;
34using HeuristicLab.Hive.Server.Core.InternalInterfaces;
35using System.Threading;
36
37namespace HeuristicLab.Hive.Server.Core {
38  /// <summary>
39  /// The ClientCommunicator manages the whole communication with the client
40  /// </summary>
41  public class ClientCommunicator: IClientCommunicator {
42    private static Dictionary<Guid, DateTime> lastHeartbeats =
43      new Dictionary<Guid,DateTime>();
44
45    private static ReaderWriterLockSlim heartbeatLock =
46      new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
47
48    private static Mutex jobLock =
49      new Mutex();
50
51    IClientAdapter clientAdapter;
52    IJobAdapter jobAdapter;
53    IJobResultsAdapter jobResultAdapter;
54    ILifecycleManager lifecycleManager;
55    IInternalJobManager jobManager;
56    IScheduler scheduler;
57
58    /// <summary>
59    /// Initialization of the Adapters to the database
60    /// Initialization of Eventhandler for the lifecycle management
61    /// Initialization of lastHearbeats Dictionary
62    /// </summary>
63    public ClientCommunicator() {
64      clientAdapter = ServiceLocator.GetClientAdapter();
65      jobAdapter = ServiceLocator.GetJobAdapter();
66      jobResultAdapter = ServiceLocator.GetJobResultsAdapter();
67      lifecycleManager = ServiceLocator.GetLifecycleManager();
68      jobManager = ServiceLocator.GetJobManager() as
69        IInternalJobManager;
70      scheduler = ServiceLocator.GetScheduler();
71
72      lifecycleManager.RegisterHeartbeat(
73        new EventHandler(lifecycleManager_OnServerHeartbeat));
74    }
75
76    /// <summary>
77    /// Check if online clients send their hearbeats
78    /// if not -> set them offline and check if they where calculating a job
79    /// </summary>
80    /// <param name="sender"></param>
81    /// <param name="e"></param>
82    void lifecycleManager_OnServerHeartbeat(object sender, EventArgs e) {
83      List<ClientInfo> allClients = new List<ClientInfo>(clientAdapter.GetAll());
84
85      foreach (ClientInfo client in allClients) {
86        if (client.State != State.offline && client.State != State.nullState) {
87          heartbeatLock.EnterUpgradeableReadLock();
88
89          if (!lastHeartbeats.ContainsKey(client.ClientId)) {
90            client.State = State.offline;
91            clientAdapter.Update(client);
92            foreach (Job job in jobAdapter.GetActiveJobsOf(client)) {
93              jobManager.ResetJobsDependingOnResults(job);
94            }
95          } else {
96            DateTime lastHbOfClient = lastHeartbeats[client.ClientId];
97
98            TimeSpan dif = DateTime.Now.Subtract(lastHbOfClient);
99            // check if time between last hearbeat and now is greather than HEARTBEAT_MAX_DIF
100            if (dif.Seconds > ApplicationConstants.HEARTBEAT_MAX_DIF) {
101              // if client calculated jobs, the job must be reset
102              if (client.State == State.calculating) {
103                // check wich job the client was calculating and reset it
104                foreach (Job job in jobAdapter.GetActiveJobsOf(client)) {
105                  jobManager.ResetJobsDependingOnResults(job);
106                }
107              }
108             
109              // client must be set offline
110              client.State = State.offline;
111              clientAdapter.Update(client);
112
113              heartbeatLock.EnterWriteLock();
114              lastHeartbeats.Remove(client.ClientId);
115              heartbeatLock.ExitWriteLock();
116            }
117          }
118
119          heartbeatLock.ExitUpgradeableReadLock();
120        } else {
121          heartbeatLock.EnterWriteLock();
122          if (lastHeartbeats.ContainsKey(client.ClientId))
123            lastHeartbeats.Remove(client.ClientId);
124          heartbeatLock.ExitWriteLock();
125        }
126      }
127    }
128
129    #region IClientCommunicator Members
130
131    /// <summary>
132    /// Login process for the client
133    /// A hearbeat entry is created as well (login is the first hearbeat)
134    /// </summary>
135    /// <param name="clientInfo"></param>
136    /// <returns></returns>
137    public Response Login(ClientInfo clientInfo) {
138      Response response = new Response();
139
140      heartbeatLock.EnterWriteLock();
141      if (lastHeartbeats.ContainsKey(clientInfo.ClientId)) {
142        lastHeartbeats[clientInfo.ClientId] = DateTime.Now;
143      } else {
144        lastHeartbeats.Add(clientInfo.ClientId, DateTime.Now);
145      }
146      heartbeatLock.ExitWriteLock();
147
148      ICollection<ClientInfo> allClients = clientAdapter.GetAll();
149      ClientInfo client = clientAdapter.GetById(clientInfo.ClientId);
150      if (client != null && client.State != State.offline && client.State != State.nullState) {
151        response.Success = false;
152        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGIN_USER_ALLREADY_ONLINE;
153        return response;
154      }
155      clientInfo.State = State.idle;
156      clientAdapter.Update(clientInfo);
157      response.Success = true;
158      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGIN_SUCCESS;
159
160      return response;
161    }
162
163    /// <summary>
164    /// The client has to send regulary heartbeats
165    /// this hearbeats will be stored in the heartbeats dictionary
166    /// check if there is work for the client and send the client a response if he should pull a job
167    /// </summary>
168    /// <param name="hbData"></param>
169    /// <returns></returns>
170    public ResponseHB SendHeartBeat(HeartBeatData hbData) {
171      ResponseHB response = new ResponseHB();
172
173      response.ActionRequest = new List<MessageContainer>();
174      if (clientAdapter.GetById(hbData.ClientId).State == State.offline ||
175          clientAdapter.GetById(hbData.ClientId).State == State.nullState) {
176        response.Success = false;
177        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_USER_NOT_LOGGED_IN;
178        response.ActionRequest.Add(new MessageContainer(MessageContainer.MessageType.NoMessage));
179        return response;
180      }
181
182      heartbeatLock.EnterWriteLock();
183      if (lastHeartbeats.ContainsKey(hbData.ClientId)) {
184        lastHeartbeats[hbData.ClientId] = DateTime.Now;
185      } else {
186        lastHeartbeats.Add(hbData.ClientId, DateTime.Now);
187      }
188      heartbeatLock.ExitWriteLock();
189
190      response.Success = true;
191      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_HEARTBEAT_RECEIVED;
192      if (hbData.freeCores > 0 && scheduler.ExistsJobForClient(hbData))
193        response.ActionRequest.Add(new MessageContainer(MessageContainer.MessageType.FetchJob));
194      else
195        response.ActionRequest.Add(new MessageContainer(MessageContainer.MessageType.NoMessage));
196
197      if (hbData.jobProgress != null) {
198        List<Job> jobsOfClient = new List<Job>(jobAdapter.GetActiveJobsOf(clientAdapter.GetById(hbData.ClientId)));
199        if (jobsOfClient == null || jobsOfClient.Count == 0) {
200          response.Success = false;
201          response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_IS_NOT_BEEING_CALCULATED;
202          return response;
203        }
204
205        foreach (KeyValuePair<long, double> jobProgress in hbData.jobProgress) {
206          Job curJob = jobAdapter.GetById(jobProgress.Key);
207          if (curJob.Client == null || curJob.Client.ClientId != hbData.ClientId) {
208            response.Success = false;
209            response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_IS_NOT_BEEING_CALCULATED;
210          } else {
211            curJob.Percentage = jobProgress.Value;
212            jobAdapter.Update(curJob);
213          }
214        }
215      }
216
217      return response;
218    }
219   
220    /// <summary>
221    /// if the client asked to pull a job he calls this method
222    /// the server selects a job and sends it to the client
223    /// </summary>
224    /// <param name="clientId"></param>
225    /// <returns></returns>
226    public ResponseJob PullJob(Guid clientId) {
227      ResponseJob response = new ResponseJob();
228
229      Job job2Calculate = scheduler.GetNextJobForClient(clientId);
230      if (job2Calculate != null) {
231        response.Job = job2Calculate;
232        response.Success = true;
233        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_PULLED;
234      } else {
235        response.Success = false;
236        response.Job = null;
237        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_NO_JOBS_LEFT;
238      }
239      return response;
240    }
241
242    /// <summary>
243    /// the client can send job results during calculating
244    /// and will send a final job result when he finished calculating
245    /// these job results will be stored in the database
246    /// </summary>
247    /// <param name="clientId"></param>
248    /// <param name="jobId"></param>
249    /// <param name="result"></param>
250    /// <param name="exception"></param>
251    /// <param name="finished"></param>
252    /// <returns></returns>
253    public ResponseResultReceived SendJobResult(Guid clientId,
254      long jobId,
255      byte[] result,
256      double percentage,
257      Exception exception, 
258      bool finished) {
259      ResponseResultReceived response = new ResponseResultReceived();
260      ClientInfo client =
261        clientAdapter.GetById(clientId);
262
263      Job job =
264        jobAdapter.GetById(jobId);
265
266      if (job.Client == null)    {
267        response.Success = false;
268        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_IS_NOT_BEEING_CALCULATED;
269        return response;
270      }
271      if (job.Client.ClientId != clientId) {
272        response.Success = false;
273        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_WRONG_CLIENT_FOR_JOB;
274        return response;
275      }
276      if (job == null) {
277        response.Success = false;
278        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_NO_JOB_WITH_THIS_ID;
279        return response;
280      }
281      if (job.State != State.calculating) {
282        response.Success = false;
283        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_WRONG_JOB_STATE;
284        return response;
285      }
286      job.SerializedJob = result;
287      job.Percentage = percentage;
288
289      if (finished) {
290        job.State = State.finished;
291        jobAdapter.Update(job);
292
293        client.State = State.idle;
294        clientAdapter.Update(client);
295
296        List<JobResult> jobResults = new List<JobResult>(jobResultAdapter.GetResultsOf(job));
297        foreach (JobResult currentResult in jobResults)
298          jobResultAdapter.Delete(currentResult);
299      }
300
301      JobResult jobResult =
302        new JobResult();
303      jobResult.Client = client;
304      jobResult.Job = job;
305      jobResult.Result = result;
306      jobResult.Percentage = percentage;
307      jobResult.Exception = exception;
308      jobResult.DateFinished = DateTime.Now;
309
310      jobResultAdapter.Update(jobResult);
311      jobAdapter.Update(job);
312
313      response.Success = true;
314      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOBRESULT_RECEIVED;
315      response.JobId = jobId;
316      response.finished = finished;
317
318      return response;
319    }
320
321    /// <summary>
322    /// when a client logs out the state will be set
323    /// and the entry in the last hearbeats dictionary will be removed
324    /// </summary>
325    /// <param name="clientId"></param>
326    /// <returns></returns>                       
327    public Response Logout(Guid clientId) {
328      Response response = new Response();
329
330      heartbeatLock.EnterWriteLock();
331      if (lastHeartbeats.ContainsKey(clientId))
332        lastHeartbeats.Remove(clientId);
333      heartbeatLock.ExitWriteLock();
334
335      ClientInfo client = clientAdapter.GetById(clientId);
336      if (client == null) {
337        response.Success = false;
338        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGOUT_CLIENT_NOT_REGISTERED;
339        return response;
340      }
341      List<Job> allJobs = new List<Job>(jobAdapter.GetAll());
342      if (client.State == State.calculating) {
343        // check wich job the client was calculating and reset it
344        foreach (Job job in allJobs) {
345          if (job.Client.ClientId == client.ClientId) {
346            jobManager.ResetJobsDependingOnResults(job);
347          }
348        }
349      }
350
351      client.State = State.offline;
352      clientAdapter.Update(client);
353
354      response.Success = true;
355      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGOUT_SUCCESS;
356     
357      return response;
358    }
359
360    #endregion
361  }
362}
Note: See TracBrowser for help on using the repository browser.