Example #1
0
        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);
        }