Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 1151 was 1141, checked in by msteinbi, 16 years ago

Implementing Lifecycle Management (#453)

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