예제 #1
0
        // Associate the service stub with the input channel which will be then used to send
        // messages to the client(s).
        public void AttachInputChannel(IDuplexInputChannel inputChannel)
        {
            using (EneterTrace.Entering())
            {
                myInputChannel = inputChannel;

                // Find events in the service interface and subscribe to them.
                EventInfo[] anEvents = typeof(TServiceInterface).GetEvents();
                foreach (EventInfo anEventInfo in anEvents)
                {
                    Type anEventArgsType = anEventInfo.EventHandlerType.IsGenericType ?
                                           anEventInfo.EventHandlerType.GetGenericArguments()[0] :
                                           typeof(EventArgs);

                    // This handler will be subscribed to events from the service.
                    // Note: for each loop create a new local variable so that the context is preserved for the Action<,> event handler.
                    //       if anEventInfo is used then the reference would be changed.
                    EventInfo aTmpEventInfo = anEventInfo;
                    Action <object, EventArgs> anEventHandler = (sender, e) =>
                    {
                        using (EneterTrace.Entering())
                        {
                            string[] aSubscribedClients = null;
                            using (ThreadLock.Lock(myServiceEvents))
                            {
                                EventContext anEventContextTmp = myServiceEvents.FirstOrDefault(x => x.EventInfo.Name == aTmpEventInfo.Name);
                                if (anEventContextTmp != null)
                                {
                                    aSubscribedClients = anEventContextTmp.SubscribedClients.ToArray();
                                }
                            }

                            // If some client is subscribed.
                            if (aSubscribedClients != null && aSubscribedClients.Length > 0)
                            {
                                object aSerializedEvent = null;

                                // If there is one serializer for all clients then pre-serialize the message to increase the performance.
                                if (mySerializer.IsSameForAllResponseReceivers())
                                {
                                    try
                                    {
                                        // Serialize the event and send it to subscribed clients.
                                        RpcMessage anEventMessage = new RpcMessage()
                                        {
                                            Id               = 0, // dummy - because we do not need to track it.
                                            Request          = ERpcRequest.RaiseEvent,
                                            OperationName    = aTmpEventInfo.Name,
                                            SerializedParams = (anEventArgsType == typeof(EventArgs)) ?
                                                               null : // EventArgs is a known type without parameters - we do not need to serialize it.
                                                               new object[] { mySerializer.Serialize(anEventArgsType, e) }
                                        };
                                        aSerializedEvent = mySerializer.Serialize <RpcMessage>(anEventMessage);
                                    }
                                    catch (Exception err)
                                    {
                                        EneterTrace.Error(TracedObject + "failed to serialize the event '" + aTmpEventInfo.Name + "'.", err);

                                        // Note: this exception will be thrown to the delegate that raised the event.
                                        throw;
                                    }
                                }

                                // Iterate via subscribed clients and send them the event.
                                foreach (string aClient in aSubscribedClients)
                                {
                                    try
                                    {
                                        // If there is serializer per client then serialize the message for each client.
                                        if (!mySerializer.IsSameForAllResponseReceivers())
                                        {
                                            ISerializer aSerializer = mySerializer.ForResponseReceiver(aClient);

                                            RpcMessage anEventMessage = new RpcMessage()
                                            {
                                                Id               = 0, // dummy - because we do not need to track it.
                                                Request          = ERpcRequest.RaiseEvent,
                                                OperationName    = aTmpEventInfo.Name,
                                                SerializedParams = (anEventArgsType == typeof(EventArgs)) ?
                                                                   null : // EventArgs is a known type without parameters - we do not need to serialize it.
                                                                   new object[] { aSerializer.Serialize(anEventArgsType, e) }
                                            };

                                            // Note: do not store serialized data to aSerializedEvent because
                                            //       if SendResponseMessage works asynchronously then the reference to serialized
                                            //       data could be overridden.
                                            object aSerializedEventForClient = aSerializer.Serialize <RpcMessage>(anEventMessage);

                                            myInputChannel.SendResponseMessage(aClient, aSerializedEventForClient);
                                        }
                                        else
                                        {
                                            myInputChannel.SendResponseMessage(aClient, aSerializedEvent);
                                        }
                                    }
                                    catch (Exception err)
                                    {
                                        EneterTrace.Error(TracedObject + "failed to send event '" + aTmpEventInfo.Name + "' to the client.", err);

                                        // Suppose the client is disconnected so unsubscribe it from all events.
                                        UnsubscribeClientFromEvents(aClient);

                                        // Note: do not retrow the exception because other subscribed clients would not be notified.
                                        //       E.g. if the exception occured because the client disconnected other clients should
                                        //       not be affected.
                                    }
                                }
                            }
                        }
                    };

                    EventContext anEventContext = null;
                    try
                    {
                        anEventContext = new EventContext(myService, anEventInfo, Delegate.CreateDelegate(anEventInfo.EventHandlerType, anEventHandler.Target, anEventHandler.Method));
                        anEventContext.Subscribe();
                    }
                    catch (Exception err)
                    {
                        string anErrorMessage = TracedObject + "failed to attach the output channel because it failed to create EventContext.";
                        EneterTrace.Error(anErrorMessage, err);
                        throw;
                    }

                    using (ThreadLock.Lock(myServiceEvents))
                    {
                        if (!myServiceEvents.Add(anEventContext))
                        {
                            string anErrorMessage = TracedObject + "failed to attach the output channel because it failed to create the event '" + anEventInfo.Name + "' because the event already exists.";
                            EneterTrace.Error(anErrorMessage);
                            throw new InvalidOperationException(anErrorMessage);
                        }
                    }
                }
            }
        }
예제 #2
0
        // This method is called when a message from the service is received.
        // This can be either the response for a request or it can be an event raised in the service.
        protected override void OnResponseMessageReceived(object sender, DuplexChannelMessageEventArgs e)
        {
            using (EneterTrace.Entering())
            {
                RpcMessage aMessage = null;
                try
                {
                    aMessage = mySerializer.ForResponseReceiver(e.ResponseReceiverId).Deserialize <RpcMessage>(e.Message);
                }
                catch (Exception err)
                {
                    EneterTrace.Error(TracedObject + "failed to deserialize incoming message.", err);
                    return;
                }

                // If it is a response for a call.
                if (aMessage.Request == ERpcRequest.Response)
                {
                    EneterTrace.Debug("RETURN FROM RPC RECEIVED");

                    // Try to find if there is a pending request waiting for the response.
                    RemoteCallContext anRpcContext;
                    using (ThreadLock.Lock(myPendingRemoteCalls))
                    {
                        myPendingRemoteCalls.TryGetValue(aMessage.Id, out anRpcContext);
                    }

                    if (anRpcContext != null)
                    {
                        if (string.IsNullOrEmpty(aMessage.ErrorType))
                        {
                            anRpcContext.SerializedReturnValue = aMessage.SerializedReturn;
                        }
                        else
                        {
                            RpcException anException = new RpcException(aMessage.ErrorMessage, aMessage.ErrorType, aMessage.ErrorDetails);
                            anRpcContext.Error = anException;
                        }

                        // Release the pending request.
                        anRpcContext.RpcCompleted.Set();
                    }
                }
                else if (aMessage.Request == ERpcRequest.RaiseEvent)
                {
                    EneterTrace.Debug("EVENT FROM SERVICE RECEIVED");

                    if (aMessage.SerializedParams != null && aMessage.SerializedParams.Length > 0)
                    {
                        // Try to raise an event.
                        // The event is raised in its own thread so that the receiving thread is not blocked.
                        // Note: raising an event cannot block handling of response messages because it can block
                        //       processing of an RPC response for which the RPC caller thread is waiting.
                        //       And if this waiting caller thread is a thread where events are routed and if the routing
                        //       of these events is 'blocking' then a deadlock can occur.
                        //       Therefore ThreadPool is used.
                        EneterThreadPool.QueueUserWorkItem(() => myThreadDispatcher.Invoke(() => RaiseEvent(aMessage.OperationName, aMessage.SerializedParams[0])));
                    }
                    else
                    {
                        // Note: this happens if the event is of type EventErgs.
                        // The event is raised in its own thread so that the receiving thread is not blocked.
                        EneterThreadPool.QueueUserWorkItem(() => myThreadDispatcher.Invoke(() => RaiseEvent(aMessage.OperationName, null)));
                    }
                }
                else
                {
                    EneterTrace.Warning(TracedObject + "detected a message with unknown flag number.");
                }
            }
        }
예제 #3
0
        public void ProcessRemoteRequest(DuplexChannelMessageEventArgs e)
        {
            using (EneterTrace.Entering())
            {
                ISerializer aSerializer = mySerializer.ForResponseReceiver(e.ResponseReceiverId);

                // Deserialize the incoming message.
                RpcMessage aRequestMessage = null;
                try
                {
                    aRequestMessage = aSerializer.Deserialize <RpcMessage>(e.Message);
                }
                catch (Exception err)
                {
                    EneterTrace.Error(TracedObject + "failed to deserialize incoming request message.", err);
                    return;
                }

                RpcMessage aResponseMessage = new RpcMessage()
                {
                    Id      = aRequestMessage.Id,
                    Request = ERpcRequest.Response
                };

                // If it is a remote call of a method/function.
                if (aRequestMessage.Request == ERpcRequest.InvokeMethod)
                {
                    EneterTrace.Debug("RPC RECEIVED");

                    // Get the method from the service that shall be invoked.
                    ServiceMethod aServiceMethod;
                    myServiceMethods.TryGetValue(aRequestMessage.OperationName, out aServiceMethod);
                    if (aServiceMethod != null)
                    {
                        if (aRequestMessage.SerializedParams != null && aRequestMessage.SerializedParams.Length == aServiceMethod.InputParameterTypes.Length)
                        {
                            // Deserialize input parameters.
                            object[] aDeserializedInputParameters = new object[aServiceMethod.InputParameterTypes.Length];
                            try
                            {
                                for (int i = 0; i < aServiceMethod.InputParameterTypes.Length; ++i)
                                {
                                    aDeserializedInputParameters[i] = aSerializer.Deserialize(aServiceMethod.InputParameterTypes[i], aRequestMessage.SerializedParams[i]);
                                }
                            }
                            catch (Exception err)
                            {
                                string anErrorMessage = "failed to deserialize input parameters for '" + aRequestMessage.OperationName + "'.";
                                EneterTrace.Error(anErrorMessage, err);

                                aResponseMessage.ErrorType    = err.GetType().Name;
                                aResponseMessage.ErrorMessage = anErrorMessage;
                                aResponseMessage.ErrorDetails = err.ToString();
                            }

                            if (string.IsNullOrEmpty(aResponseMessage.ErrorType))
                            {
                                object aResult = null;
                                try
                                {
                                    // Invoke the service method.
                                    aResult = aServiceMethod.Method.Invoke(myService, aDeserializedInputParameters);
                                }
                                catch (Exception err)
                                {
                                    // Note: Use InnerException to skip the wrapping ReflexionException.
                                    Exception ex = (err.InnerException != null) ? err.InnerException : err;

                                    EneterTrace.Error(TracedObject + ErrorHandler.DetectedException, ex);

                                    // The exception will be responded to the client.
                                    aResponseMessage.ErrorType    = ex.GetType().Name;
                                    aResponseMessage.ErrorMessage = ex.Message;
                                    aResponseMessage.ErrorDetails = ex.ToString();
                                }

                                if (string.IsNullOrEmpty(aResponseMessage.ErrorType))
                                {
                                    try
                                    {
                                        // Serialize the result.
                                        if (aServiceMethod.Method.ReturnType != typeof(void))
                                        {
                                            aResponseMessage.SerializedReturn = aSerializer.Serialize(aServiceMethod.Method.ReturnType, aResult);
                                        }
                                        else
                                        {
                                            aResponseMessage.SerializedReturn = null;
                                        }
                                    }
                                    catch (Exception err)
                                    {
                                        string anErrorMessage = TracedObject + "failed to serialize the result.";
                                        EneterTrace.Error(anErrorMessage, err);

                                        aResponseMessage.ErrorType    = err.GetType().Name;
                                        aResponseMessage.ErrorMessage = anErrorMessage;
                                        aResponseMessage.ErrorDetails = err.ToString();
                                    }
                                }
                            }
                        }
                        else
                        {
                            aResponseMessage.ErrorType    = typeof(InvalidOperationException).Name;
                            aResponseMessage.ErrorMessage = TracedObject + "failed to process '" + aRequestMessage.OperationName + "' because it has incorrect number of input parameters.";
                            EneterTrace.Error(aResponseMessage.ErrorMessage);
                        }
                    }
                    else
                    {
                        aResponseMessage.ErrorType    = typeof(InvalidOperationException).Name;
                        aResponseMessage.ErrorMessage = "Method '" + aRequestMessage.OperationName + "' does not exist in the service.";
                        EneterTrace.Error(aResponseMessage.ErrorMessage);
                    }
                }
                // If it is a request to subscribe/unsubcribe an event.
                else if (aRequestMessage.Request == ERpcRequest.SubscribeEvent || aRequestMessage.Request == ERpcRequest.UnsubscribeEvent)
                {
                    EventContext anEventContext = null;
                    using (ThreadLock.Lock(myServiceEvents))
                    {
                        anEventContext = myServiceEvents.FirstOrDefault(x => x.EventInfo.Name == aRequestMessage.OperationName);
                        if (anEventContext != null)
                        {
                            if (aRequestMessage.Request == ERpcRequest.SubscribeEvent)
                            {
                                EneterTrace.Debug("SUBSCRIBE REMOTE EVENT RECEIVED");

                                // Note: Events are added to the HashSet.
                                //       Therefore it is ensured each client is subscribed only once.
                                anEventContext.SubscribedClients.Add(e.ResponseReceiverId);
                            }
                            else
                            {
                                EneterTrace.Debug("UNSUBSCRIBE REMOTE EVENT RECEIVED");

                                anEventContext.SubscribedClients.Remove(e.ResponseReceiverId);
                            }
                        }
                    }

                    if (anEventContext == null)
                    {
                        aResponseMessage.ErrorType    = typeof(InvalidOperationException).Name;
                        aResponseMessage.ErrorMessage = TracedObject + "Event '" + aRequestMessage.OperationName + "' does not exist in the service.";
                        EneterTrace.Error(aResponseMessage.ErrorMessage);
                    }
                }
                else
                {
                    aResponseMessage.ErrorType    = typeof(InvalidOperationException).Name;
                    aResponseMessage.ErrorMessage = TracedObject + "could not recognize the incoming request. If it is RPC, Subscribing or Unsubscribfing.";
                    EneterTrace.Error(aResponseMessage.ErrorMessage);
                }


                try
                {
                    // Serialize the response message.
                    object aSerializedResponse = aSerializer.Serialize <RpcMessage>(aResponseMessage);
                    myInputChannel.SendResponseMessage(e.ResponseReceiverId, aSerializedResponse);
                }
                catch (Exception err)
                {
                    EneterTrace.Error(TracedObject + "." + aRequestMessage.OperationName + " " + ErrorHandler.FailedToSendResponseMessage, err);
                }
            }
        }
예제 #4
0
        private object SerializeRpcMessage(RpcMessage rpcMessage)
        {
            using (MemoryStream aStream = new MemoryStream())
            {
                BinaryWriter aWriter = new BinaryWriter(aStream);

                // Write Id of the request.
                myEncoderDecoder.WriteInt32(aWriter, rpcMessage.Id, myIsLittleEndian);

                // Write request flag.
                byte aRequestType = (byte)rpcMessage.Request;
                aWriter.Write(aRequestType);

                if (rpcMessage.Request == ERpcRequest.InvokeMethod ||
                    rpcMessage.Request == ERpcRequest.RaiseEvent)
                {
                    // Write name of the method or name of the event which shall be raised.
                    myEncoderDecoder.WritePlainString(aWriter, rpcMessage.OperationName, Encoding.UTF8, myIsLittleEndian);

                    // Write number of input parameters.
                    if (rpcMessage.SerializedParams == null)
                    {
                        myEncoderDecoder.WriteInt32(aWriter, 0, myIsLittleEndian);
                    }
                    else
                    {
                        myEncoderDecoder.WriteInt32(aWriter, rpcMessage.SerializedParams.Length, myIsLittleEndian);
                        // Write already serialized input parameters.
                        for (int i = 0; i < rpcMessage.SerializedParams.Length; ++i)
                        {
                            myEncoderDecoder.Write(aWriter, rpcMessage.SerializedParams[i], myIsLittleEndian);
                        }
                    }
                }
                else if (rpcMessage.Request == ERpcRequest.SubscribeEvent ||
                         rpcMessage.Request == ERpcRequest.UnsubscribeEvent)
                {
                    // Write name of the event which shall be subscribed or unsubcribed.
                    myEncoderDecoder.WritePlainString(aWriter, rpcMessage.OperationName, Encoding.UTF8, myIsLittleEndian);
                }
                else if (rpcMessage.Request == ERpcRequest.Response)
                {
                    // Write already serialized return value.
                    if (rpcMessage.SerializedReturn != null)
                    {
                        // Indicate it is not void.
                        aWriter.Write((byte)1);

                        // Wtrite return value.
                        myEncoderDecoder.Write(aWriter, rpcMessage.SerializedReturn, myIsLittleEndian);
                    }
                    else
                    {
                        // Indicate there is no rturn value.
                        aWriter.Write((byte)0);
                    }

                    if (!string.IsNullOrEmpty(rpcMessage.ErrorType))
                    {
                        // Indicate the response contains the error message.
                        aWriter.Write((byte)1);

                        // Write error.
                        myEncoderDecoder.WritePlainString(aWriter, rpcMessage.ErrorType, Encoding.UTF8, myIsLittleEndian);
                        myEncoderDecoder.WritePlainString(aWriter, rpcMessage.ErrorMessage, Encoding.UTF8, myIsLittleEndian);
                        myEncoderDecoder.WritePlainString(aWriter, rpcMessage.ErrorDetails, Encoding.UTF8, myIsLittleEndian);
                    }
                    else
                    {
                        // Indicate the response does not contain the error message.
                        aWriter.Write((byte)0);
                    }
                }

                return(aStream.ToArray());
            }
        }