public override bool Init() { log.Info("()"); bool res = false; try { client = new LocClient(Config.Configuration.LocEndPoint, new LocMessageProcessor(), ShutdownSignaling); this.Location = Config.Configuration.LocLocation; locConnectionThread = new Thread(new ThreadStart(LocConnectionThread)); locConnectionThread.Start(); RegisterCronJobs(); res = true; Initialized = true; } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); } if (!res) { ShutdownSignaling.SignalShutdown(); if ((locConnectionThread != null) && !locConnectionThreadFinished.WaitOne(10000)) { log.Error("LOC connection thread did not terminated in 10 seconds."); } } log.Info("(-):{0}", res); return(res); }
/// <summary> /// Processing of a message received from a client. /// </summary> /// <param name="Client">TCP client who received the message.</param> /// <param name="IncomingMessage">Full ProtoBuf message to be processed.</param> /// <returns>true if the conversation with the client should continue, false if a protocol violation error occurred and the client should be disconnected.</returns> public async Task <bool> ProcessMessageAsync(ClientBase Client, IProtocolMessage IncomingMessage) { LocClient client = (LocClient)Client; LocProtocolMessage incomingMessage = (LocProtocolMessage)IncomingMessage; log.Debug("()"); bool res = false; try { log.Trace("Received message type is {0}, message ID is {1}.", incomingMessage.MessageTypeCase, incomingMessage.Id); switch (incomingMessage.MessageTypeCase) { case Message.MessageTypeOneofCase.Request: { LocProtocolMessage responseMessage = client.MessageBuilder.CreateErrorProtocolViolationResponse(incomingMessage); Request request = incomingMessage.Request; SemVer version = new SemVer(request.Version); log.Trace("Request type is {0}, version is {1}.", request.RequestTypeCase, version); switch (request.RequestTypeCase) { case Request.RequestTypeOneofCase.LocalService: { log.Trace("Local service request type is {0}.", request.LocalService.LocalServiceRequestTypeCase); switch (request.LocalService.LocalServiceRequestTypeCase) { case LocalServiceRequest.LocalServiceRequestTypeOneofCase.NeighbourhoodChanged: { responseMessage = await ProcessMessageNeighbourhoodChangedNotificationRequestAsync(client, incomingMessage); break; } default: log.Warn("Invalid local service request type '{0}'.", request.LocalService.LocalServiceRequestTypeCase); break; } break; } default: log.Warn("Invalid request type '{0}'.", request.RequestTypeCase); break; } if (responseMessage != null) { // Send response to client. res = await client.SendMessageAsync(responseMessage); if (res) { // If the message was sent successfully to the target, we close the connection only in case of protocol violation error. if (responseMessage.MessageTypeCase == Message.MessageTypeOneofCase.Response) { res = responseMessage.Response.Status != Status.ErrorProtocolViolation; } } } else { // If there is no response to send immediately to the client, // we want to keep the connection open. res = true; } break; } case Message.MessageTypeOneofCase.Response: { Response response = incomingMessage.Response; log.Trace("Response status is {0}, details are '{1}', response type is {2}.", response.Status, response.Details, response.ResponseTypeCase); // The only response we should ever receive here is GetNeighbourNodesByDistanceResponse in response to our refresh request that we do from time to time. bool isGetNeighbourNodesByDistanceResponse = (response.Status == Status.Ok) && (response.ResponseTypeCase == Response.ResponseTypeOneofCase.LocalService) && (response.LocalService.LocalServiceResponseTypeCase == LocalServiceResponse.LocalServiceResponseTypeOneofCase.GetNeighbourNodes); if (!isGetNeighbourNodesByDistanceResponse) { log.Error("Unexpected response type {0} received, status code {1}.", response.ResponseTypeCase, response.Status); break; } // Process the response. res = await ProcessMessageGetNeighbourNodesByDistanceResponseAsync(incomingMessage, false); break; } default: log.Error("Unknown message type '{0}', connection to the client will be closed.", incomingMessage.MessageTypeCase); await SendProtocolViolation(client); // Connection will be closed in ReceiveMessageLoop. break; } } catch (Exception e) { log.Error("Exception occurred, connection to the client will be closed: {0}", e.ToString()); await SendProtocolViolation(client); // Connection will be closed in ReceiveMessageLoop. } log.Debug("(-):{0}", res); return(res); }
/// <summary> /// Processes NeighbourhoodChangedNotificationRequest message from LOC server. /// <para>Adds, changes, or deletes neighbor and possibly adds new neighborhood action to the database.</para> /// </summary> /// <param name="Client">TCP client who received the message.</param> /// <param name="RequestMessage">Full request message.</param> /// <returns>Response message to be sent to the client.</returns> public async Task <LocProtocolMessage> ProcessMessageNeighbourhoodChangedNotificationRequestAsync(LocClient Client, LocProtocolMessage RequestMessage) { log.Trace("()"); LocProtocolMessage res = Client.MessageBuilder.CreateErrorInternalResponse(RequestMessage); bool signalActionProcessor = false; NeighbourhoodChangedNotificationRequest neighbourhoodChangedNotificationRequest = RequestMessage.Request.LocalService.NeighbourhoodChanged; using (UnitOfWork unitOfWork = new UnitOfWork()) { DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.NeighborLock, UnitOfWork.NeighborhoodActionLock }; using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects)) { bool success = false; bool saveDb = false; try { int neighborhoodSize = await unitOfWork.NeighborRepository.CountAsync(); foreach (NeighbourhoodChange change in neighbourhoodChangedNotificationRequest.Changes) { // We do ignore errors here for each individual change and just continue processing the next item from the list. log.Trace("Neighborhood change type is {0}.", change.ChangeTypeCase); switch (change.ChangeTypeCase) { case NeighbourhoodChange.ChangeTypeOneofCase.AddedNodeInfo: case NeighbourhoodChange.ChangeTypeOneofCase.UpdatedNodeInfo: { bool isAdd = change.ChangeTypeCase == NeighbourhoodChange.ChangeTypeOneofCase.AddedNodeInfo; NodeInfo nodeInfo = isAdd ? change.AddedNodeInfo : change.UpdatedNodeInfo; // Check whether a proximity server is running on this node. // If not, it is not interesting for us at all, skip it. int proximityServerPort; byte[] proximityServerId; if (!HasProximityServerService(nodeInfo, out proximityServerPort, out proximityServerId)) { break; } NodeContact contact = nodeInfo.Contact; IPAddress ipAddress = new IPAddress(contact.IpAddress.ToByteArray()); Iop.Locnet.GpsLocation location = nodeInfo.Location; int latitude = location.Latitude; int longitude = location.Longitude; AddOrChangeNeighborResult addChangeRes = await AddOrChangeNeighborAsync(unitOfWork, proximityServerId, ipAddress, proximityServerPort, latitude, longitude, neighborhoodSize); neighborhoodSize = addChangeRes.NeighborhoodSize; if (addChangeRes.SaveDb) { saveDb = true; } if (addChangeRes.SignalActionProcessor) { signalActionProcessor = true; } break; } case NeighbourhoodChange.ChangeTypeOneofCase.RemovedNodeId: { byte[] serverId = change.RemovedNodeId.ToByteArray(); bool serverIdValid = serverId.Length == ProtocolHelper.NetworkIdentifierLength; if (!serverIdValid) { log.Error("Received invalid neighbor server ID '{0}' from LOC server.", serverId.ToHex()); break; } // Data processing. Neighbor existingNeighbor = (await unitOfWork.NeighborRepository.GetAsync(n => n.NetworkId == serverId)).FirstOrDefault(); if (existingNeighbor != null) { log.Trace("Creating neighborhood action to deleting neighbor ID '{0}' from the database.", serverId.ToHex()); string neighborInfo = JsonConvert.SerializeObject(existingNeighbor); // Delete neighbor completely. // This will cause our proximity server to erase all activities of the neighbor that has been removed. bool deleted = await unitOfWork.NeighborRepository.DeleteNeighborAsync(serverId, -1, true); if (deleted) { // Add action that will contact the neighbor and ask it to stop sending updates. // Note that the neighbor information will be deleted by the time this action // is executed and this is why we have to fill in AdditionalData. NeighborhoodAction stopUpdatesAction = new NeighborhoodAction() { ServerId = serverId, Timestamp = DateTime.UtcNow, Type = NeighborhoodActionType.StopNeighborhoodUpdates, TargetActivityId = 0, TargetActivityOwnerId = null, ExecuteAfter = DateTime.UtcNow, AdditionalData = neighborInfo }; await unitOfWork.NeighborhoodActionRepository.InsertAsync(stopUpdatesAction); signalActionProcessor = true; saveDb = true; } else { log.Error("Failed to remove neighbor ID '{0}' from the database.", serverId.ToHex()); // This is actually bad, we failed to remove a record from the database, which should never happen. // We try to insert action to remove this neighbor later, but adding the action might fail as well. NeighborhoodAction action = new NeighborhoodAction() { ServerId = serverId, Timestamp = DateTime.UtcNow, Type = NeighborhoodActionType.RemoveNeighbor, TargetActivityId = 0, TargetActivityOwnerId = null, AdditionalData = null }; await unitOfWork.NeighborhoodActionRepository.InsertAsync(action); signalActionProcessor = true; saveDb = true; } } else { log.Debug("Neighbor ID '{0}' not found, can not be removed.", serverId.ToHex()); // It can be the case that this node has not an associated proximity server, so in that case we should ignore it. // If the node has an associated proximity server, then nothing bad really happens here if we have activities // of such a neighbor in NeighborActivity table. Those entries will expire and will be deleted. } break; } default: log.Error("Invalid neighborhood change type '{0}'.", change.ChangeTypeCase); break; } } if (saveDb) { await unitOfWork.SaveThrowAsync(); transaction.Commit(); } success = true; res = Client.MessageBuilder.CreateNeighbourhoodChangedNotificationResponse(RequestMessage); } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); } if (!success) { log.Warn("Rolling back transaction."); unitOfWork.SafeTransactionRollback(transaction); } unitOfWork.ReleaseLock(lockObjects); } } if (signalActionProcessor) { NeighborhoodActionProcessor neighborhoodActionProcessor = (NeighborhoodActionProcessor)Base.ComponentDictionary[NeighborhoodActionProcessor.ComponentName]; neighborhoodActionProcessor.Signal(); } log.Trace("(-):*.Response.Status={0}", res.Response.Status); return(res); }