public void TestRemoteMethodCallSerialization()
        {
            var call = new RemoteMethodCall();

            call.ComponentName = "component";
            call.CallId = Guid.NewGuid();
            call.MethodName = "sampleMethod";
            call.Parameters = new List<object> { 1, 2, 3 };

            var xml = call.Serialize();

            var roundtrip = new RemoteMethodCall();
            Assert.IsTrue(roundtrip.Validate(xml));
            roundtrip.Deserialize(xml);

            Assert.IsNotNull(roundtrip);
            Assert.AreEqual<Guid>(call.CallId, roundtrip.CallId);
            Assert.AreEqual<string>(call.ComponentName, roundtrip.ComponentName);
            Assert.AreEqual<string>(call.MethodName, roundtrip.MethodName);
            Assert.AreEqual<int>(call.Parameters.Count(), roundtrip.Parameters.Count());

            int count = call.Parameters.Count();
            for (int i = 0; i < count; i++)
            {
                Assert.AreEqual<int>((int)call.Parameters.Skip(i).First(), (int)roundtrip.Parameters.Skip(i).First());
            }
        }
Beispiel #2
0
        /// <summary>
        /// Processes an incoming RemoteMethodResponse
        /// </summary>
        /// <param name="response">The RemoteMethodResponse that is to be processed</param>
        protected virtual void processRemoteMethodResponse(RemoteMethodResponse response)
        {
            //get the RemoteMethodCall that triggered the response from the cache
            RemoteMethodCall call = unrepliedCalls.ContainsKey(response.CallId) ? unrepliedCalls[response.CallId] : null;

            //response is only processed, when the associated method call could be found
            if (call != null)
            {
                //stop the connection from timing out as we just received a message
                timeoutTimer.Stop();

                //if call was synchronous => put the response value in the value cache and release wait handle
                if (synchronousCalls_WaitHandles.ContainsKey(call.CallId))
                {
                    synchonousCalls_ValueCache.Add(call.CallId, response.ResponseValue);
                    synchronousCalls_WaitHandles[call.CallId].Set();
                }

                //remove call from unreplied calls
                unrepliedCalls.Remove(response.CallId);

                //decrease the number of repsonses expected from the target client (connection will not time out when no responses are expected
                if (call.ResponseExpected)
                {
                    this.expectedResponseCount--;
                }

                //if there are still call taht are unreplied, restart the timeout-timer
                if (unrepliedCalls.Any())
                {
                    timeoutTimer.Start();
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Send a message to the target client
        /// </summary>
        /// <param name="msg">The message to process and send</param>
        protected virtual void sendMessage(RpcMessage msg)
        {
            //if message is a RemoteMethodCall, it will be cached to be able to process response messages
            if (msg is RemoteMethodCall)
            {
                RemoteMethodCall call = msg as RemoteMethodCall;
                if (call.ResponseExpected)
                {
                    unrepliedCalls.Add(call.CallId, call);
                    expectedResponseCount++;
                    timeoutTimer.Start();
                }
            }

            //process the message using the MessageProcessor and pass the result to the ConnectionManager to send
            try
            {
                this.WtlpClient.Send(msg.Serialize().ToString().GetBytesUTF8());
            }
            catch (WtlpException ex)
            {
                if (ex.Error == Result.Timeout)
                {
                    throw new TimeoutException();
                }
                else
                {
                    throw new RemoteErrorException();
                }
            }
        }
        public void TestRemoteMethodCallSerialization_MissingComponentNameElement()
        {
            var call = new RemoteMethodCall();

            call.ComponentName = "component";
            call.CallId = Guid.NewGuid();
            call.MethodName = "sampleMethod";
            call.Parameters = new List<object> { 1, 2, 3 };

            var xml = call.Serialize();

            xml.Element(XName.Get("ComponentName", xml.Name.NamespaceName)).Remove();

            var roundtrip = new RemoteMethodCall();
            Assert.IsFalse(roundtrip.Validate(xml));
        }
Beispiel #5
0
        protected object invokeRemoteMethod(string component, string name, object[] args, bool responseExpected)
        {
            var call = new RemoteMethodCall()
            {
                ComponentName = component, MethodName = name, Parameters = args.ToList <object>(), ResponseExpected = responseExpected
            };

            //send the RemoteMethodCall
            sendMessage(call);

            //if a response is expected, wait for the recipient to send a response
            if (responseExpected)
            {
                //wait for the response using a EventWaitHandle
                if (!synchronousCalls_WaitHandles.ContainsKey(call.CallId))
                {
                    synchronousCalls_WaitHandles.Add(call.CallId, new EventWaitHandle(false, EventResetMode.ManualReset));
                }

                synchronousCalls_WaitHandles[call.CallId].WaitOne();

                //get the returned value from the cache
                object value = synchonousCalls_ValueCache[call.CallId];

                //remove the value from value cache
                synchonousCalls_ValueCache.Remove(call.CallId);

                if (value is TimeoutException)
                {
                    throw (value as TimeoutException);
                }
                else if (value is RemoteError)
                {
                    throw new RemoteErrorException(value as RemoteError);
                }
                else
                {
                    return(value);
                }
            }
            else
            {
                return(null);
            }
        }
Beispiel #6
0
        /// <summary>
        /// Processes an incoming RemoteMethodCall
        /// </summary>
        /// <param name="call">The RemoteMethodCall that is to be processed</param>
        /// <param name="component">The component that will handle the call</param>
        protected virtual void processRemoteMethodCall(RemoteMethodCall call, IComponent component)
        {
            //get the call's result
            CallResult callResult = getCallResult(call, component);

            if (callResult is ErrorResult)
            {
                //result was error => send RemoteError
                sendMessage(
                    new RemoteError((callResult as ErrorResult).ErrorCode)
                {
                    CallId        = call.CallId,
                    ComponentName = call.ComponentName
                }
                    );
            }
            else if (callResult is ResponseResult)
            {
                //result was a response => send RemoteMethodResponse
                RemoteMethodResponse response = new RemoteMethodResponse();
                response.CallId        = call.CallId;
                response.ComponentName = call.ComponentName;
                response.ResponseValue = (callResult as ResponseResult).ResponseValue;

                sendMessage(response);
            }
            else if (callResult is VoidResult)
            {
                //result was VoidResult => do nothing
            }
            else
            {
                //should not happen, ResponseResult, ErrorResult and VoidResult are the only known subclasses of CallResult the
                logger.Error("ProcessMessage() encountered an unknown type of CallResult");
            }

            //If a post-processing action has been specified in the call, invoke it now
            if (callResult.PostProcessingAction != null)
            {
                callResult.PostProcessingAction.Invoke();
            }
        }
Beispiel #7
0
        /// <summary>
        /// Tries to retrive a <see cref="CallResult"/> from the specified component for the speicified RemoteMethodCall using Reflection
        /// </summary>
        /// <param name="call">The RemoteMethodCall that needs to be answered</param>
        /// <param name="component">The component the RemoteMethodCall targeted</param>
        /// <returns>Returns the <see cref="CallResult"/> for the specified RemoteMethodCall</returns>
        protected virtual CallResult getCallResult(RemoteMethodCall call, IComponent component)
        {
            //search the component for a method taht can handle the call
            Dictionary <string, MethodInfo> callHandlers = new Dictionary <string, MethodInfo>();

            foreach (MethodInfo mi in component.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
            {
                object[] attributes = mi.GetCustomAttributes(typeof(MethodCallHandlerAttribute), true);
                foreach (MethodCallHandlerAttribute attr in attributes)
                {
                    if (!callHandlers.ContainsKey(attr.MethodName))
                    {
                        callHandlers.Add(attr.MethodName, mi);
                    }
                }
            }


            if (callHandlers.ContainsKey(call.MethodName))
            {
                var attributes = callHandlers[call.MethodName].GetCustomAttributes(typeof(TrustLevelAttribute), true);

                //get the trust level from the method's TrustLevel attribute
                TrustLevelAttribute trustLevel;
                if (attributes.Any())
                {
                    trustLevel = (TrustLevelAttribute)attributes.First();
                }
                else
                {
                    //if no attribute could be found, fall back to the default (highest possible trustlevel)
                    trustLevel = TrustLevelAttribute.MAX;
                }

                //check if the sender is authorized to make that method call
                if (trustLevel.RequiredTrustLevel > this.TrustLevel)
                {
                    return(new ErrorResult(RemoteErrorCode.NotAuthorizedError));
                }
                else
                {
                    //invoke the call handler method and return it's result
                    try
                    {
                        return((CallResult)callHandlers[call.MethodName].Invoke(component, call.Parameters.ToArray <object>()));
                    }
                    catch (Exception e)
                    {
                        if (e.GetType() == typeof(ArgumentException) || e.GetType() == typeof(TargetParameterCountException))
                        {
                            return(new ErrorResult(RemoteErrorCode.InvalidParametersError));
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }
            //Invoked method could not be found => return ErrorResult
            else
            {
                return(new ErrorResult(RemoteErrorCode.MethodNotFoundError));
            }
        }
Beispiel #8
0
        /// <summary>
        /// Processes an incoming message (using the MessageProcessor for deserialization)
        /// </summary>
        /// <param name="message">The message to be processed</param>
        private void processMessage(ParsingResult message)
        {
            //parse the message
            XElement   xmlMessage;
            RpcMessage msg  = null;
            var        body = message.Payload.ToStringUTF8();

            try
            {
                //try to parse it as XML
                xmlMessage = XElement.Parse(body);

                switch (xmlMessage.Name.LocalName.ToLower())
                {
                case "remotemethodcall":
                    msg = new RemoteMethodCall();
                    break;

                case "remotemethodresponse":
                    msg = new RemoteMethodResponse();
                    break;

                case "remoteerror":
                    msg = new RemoteError();
                    break;
                }

                if (!msg.Validate(xmlMessage))
                {
                    logger.Error("Message from {0} could not be validated", this.Target);
                    var error = new RemoteError(RemoteErrorCode.InvalidXmlError);
                    sendMessage(error);
                    return;
                }

                msg.Deserialize(xmlMessage);
            }
            //Catch XmlException and wrap it into a format exception (makes catching errors in the caller easier)
            catch (XmlException)
            {
                logger.Error("Could not parse message received from {0}", this.Target);
                RemoteError error = new RemoteError(RemoteErrorCode.InvalidXmlError);
                sendMessage(error);
                return;
            }



            //encryption is mandatory if TrustLevel is 2 or higher
            //if message was not encrypted it will result in a RemoteError and the connection will be reset
            if (this.TrustLevel >= 2 && !message.WasEncrypted && !(msg is RemoteMethodCall && (msg as RemoteMethodCall).MethodName == CoreMethods.SendResetNotice))
            {
                RemoteError error = new RemoteError(RemoteErrorCode.EncryptionError);
                if (msg.CallId != Guid.Empty)
                {
                    error.CallId = msg.CallId;
                }
                error.ComponentName = "Authentication";

                //The RemoteError will not be encrypted
                WtlpClient.EncryptMessages = false;
                sendMessage(error);
                ResetConnection();
                return;
            }



            if (msg is RemoteMethodCall)
            {
                //get the component responsible for handling the message
                string componentName = (msg as RpcMessage).ComponentName;
                var    component     = GetServerComponent(componentName);

                if (component == null)
                {
                    //no component to handle the request was found => send a RemoteError as response and return
                    RemoteError error = new RemoteError(RemoteErrorCode.ComponentNotFoundError);
                    error.CallId = (msg as RemoteMethodCall).CallId;
                    sendMessage(error);
                    return;
                }

                var processingTask = new Task(delegate
                {
                    processRemoteMethodCall(msg as RemoteMethodCall, component);
                });

                processingTask.Start();

                var heartBeatTask = new Task(delegate
                {
                    var coreComponent = new CoreClientComponent();
                    coreComponent.ClientConnection = this;
                    while (!processingTask.IsCompleted)
                    {
                        Thread.Sleep(25000);
                        if (!processingTask.IsCompleted)
                        {
                            coreComponent.HeartbeatAsync();
                        }
                    }
                });

                heartBeatTask.Start();
            }
            else if (msg is RemoteMethodResponse)
            {
                processRemoteMethodResponse(msg as RemoteMethodResponse);
            }
            else if (msg is RemoteError)
            {
                processRemoteError(msg as RemoteError);
            }
            else
            {
                logger.Error("ProcessMessage() encoutered an unknown type of Message");
                sendMessage(new RemoteError(RemoteErrorCode.UnknownMessage));
            }
        }
        /// <summary>
        /// Processes an incoming message (using the MessageProcessor for deserialization)
        /// </summary>
        /// <param name="message">The message to be processed</param>
        private void processMessage(ParsingResult message)
        {
            //parse the message
            XElement xmlMessage;
            RpcMessage msg = null;
            var body = message.Payload.ToStringUTF8();
            try
            {
                //try to parse it as XML
                xmlMessage = XElement.Parse(body);

                switch (xmlMessage.Name.LocalName.ToLower())
                {
                    case "remotemethodcall":
                        msg = new RemoteMethodCall();
                        break;
                    case "remotemethodresponse":
                        msg = new RemoteMethodResponse();
                        break;
                    case "remoteerror":
                        msg = new RemoteError();
                        break;
                }

                if (!msg.Validate(xmlMessage))
                {
                    logger.Error("Message from {0} could not be validated", this.Target);
                    var error = new RemoteError(RemoteErrorCode.InvalidXmlError);
                    sendMessage(error);
                    return;
                }

                msg.Deserialize(xmlMessage);

            }
            //Catch XmlException and wrap it into a format exception (makes catching errors in the caller easier)
            catch (XmlException)
            {
                logger.Error("Could not parse message received from {0}", this.Target);
                RemoteError error = new RemoteError(RemoteErrorCode.InvalidXmlError);
                sendMessage(error);
                return;
            }

            //encryption is mandatory if TrustLevel is 2 or higher
            //if message was not encrypted it will result in a RemoteError and the connection will be reset
            if (this.TrustLevel >= 2 && !message.WasEncrypted && !(msg is RemoteMethodCall && (msg as RemoteMethodCall).MethodName == CoreMethods.SendResetNotice))
            {
                RemoteError error = new RemoteError(RemoteErrorCode.EncryptionError);
                if (msg.CallId != Guid.Empty)
                    error.CallId = msg.CallId;
                error.ComponentName = "Authentication";

                //The RemoteError will not be encrypted
                WtlpClient.EncryptMessages = false;
                sendMessage(error);
                ResetConnection();
                return;
            }

            if (msg is RemoteMethodCall)
            {
                //get the component responsible for handling the message
                string componentName = (msg as RpcMessage).ComponentName;
                var component = GetServerComponent(componentName);

                if (component == null)
                {
                    //no component to handle the request was found => send a RemoteError as response and return
                    RemoteError error = new RemoteError(RemoteErrorCode.ComponentNotFoundError);
                    error.CallId = (msg as RemoteMethodCall).CallId;
                    sendMessage(error);
                    return;
                }

                var processingTask = new Task(delegate
                    {
                        processRemoteMethodCall(msg as RemoteMethodCall, component);
                    });

                processingTask.Start();

                var heartBeatTask = new Task(delegate
                    {
                        var coreComponent = new CoreClientComponent();
                        coreComponent.ClientConnection = this;
                        while (!processingTask.IsCompleted)
                        {
                            Thread.Sleep(25000);
                            if (!processingTask.IsCompleted)
                                coreComponent.HeartbeatAsync();
                        }
                    });

                heartBeatTask.Start();
            }
            else if (msg is RemoteMethodResponse)
            {
                processRemoteMethodResponse(msg as RemoteMethodResponse);
            }
            else if (msg is RemoteError)
            {
                processRemoteError(msg as RemoteError);
            }
            else
            {
                logger.Error("ProcessMessage() encoutered an unknown type of Message");
                sendMessage(new RemoteError(RemoteErrorCode.UnknownMessage));
            }
        }
        /// <summary>
        /// Processes an incoming RemoteMethodCall
        /// </summary>
        /// <param name="call">The RemoteMethodCall that is to be processed</param>
        /// <param name="component">The component that will handle the call</param>
        protected virtual void processRemoteMethodCall(RemoteMethodCall call, IComponent component)
        {
            //get the call's result
            CallResult callResult = getCallResult(call, component);

            if (callResult is ErrorResult)
            {
                //result was error => send RemoteError
                sendMessage(
                    new RemoteError((callResult as ErrorResult).ErrorCode)
                        {
                            CallId = call.CallId,
                            ComponentName = call.ComponentName
                        }
                    );
            }
            else if (callResult is ResponseResult)
            {
                //result was a response => send RemoteMethodResponse
                RemoteMethodResponse response = new RemoteMethodResponse();
                response.CallId = call.CallId;
                response.ComponentName = call.ComponentName;
                response.ResponseValue = (callResult as ResponseResult).ResponseValue;

                sendMessage(response);
            }
            else if (callResult is VoidResult)
            {
                //result was VoidResult => do nothing
            }
            else
            {
                //should not happen, ResponseResult, ErrorResult and VoidResult are the only known subclasses of CallResult the
                logger.Error("ProcessMessage() encountered an unknown type of CallResult");
            }

            //If a post-processing action has been specified in the call, invoke it now
            if (callResult.PostProcessingAction != null)
            {
                callResult.PostProcessingAction.Invoke();
            }
        }
        protected object invokeRemoteMethod(string component, string name, object[] args, bool responseExpected)
        {
            var call = new RemoteMethodCall() { ComponentName = component, MethodName = name, Parameters = args.ToList<object>(), ResponseExpected = responseExpected };

            //send the RemoteMethodCall
            sendMessage(call);

            //if a response is expected, wait for the recipient to send a response
            if (responseExpected)
            {
                //wait for the response using a EventWaitHandle
                if (!synchronousCalls_WaitHandles.ContainsKey(call.CallId))
                    synchronousCalls_WaitHandles.Add(call.CallId, new EventWaitHandle(false, EventResetMode.ManualReset));

                synchronousCalls_WaitHandles[call.CallId].WaitOne();

                //get the returned value from the cache
                object value = synchonousCalls_ValueCache[call.CallId];

                //remove the value from value cache
                synchonousCalls_ValueCache.Remove(call.CallId);

                if (value is TimeoutException)
                {
                    throw (value as TimeoutException);
                }
                else if (value is RemoteError)
                {
                    throw new RemoteErrorException(value as RemoteError);
                }
                else
                {
                    return value;
                }
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// Tries to retrive a <see cref="CallResult"/> from the specified component for the speicified RemoteMethodCall using Reflection
        /// </summary>
        /// <param name="call">The RemoteMethodCall that needs to be answered</param>
        /// <param name="component">The component the RemoteMethodCall targeted</param>
        /// <returns>Returns the <see cref="CallResult"/> for the specified RemoteMethodCall</returns>
        protected virtual CallResult getCallResult(RemoteMethodCall call, IComponent component)
        {
            //search the component for a method taht can handle the call
            Dictionary<string, MethodInfo> callHandlers = new Dictionary<string, MethodInfo>();
            foreach (MethodInfo mi in component.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
            {
                object[] attributes = mi.GetCustomAttributes(typeof(MethodCallHandlerAttribute), true);
                foreach (MethodCallHandlerAttribute attr in attributes)
                {
                    if (!callHandlers.ContainsKey(attr.MethodName))
                    {
                        callHandlers.Add(attr.MethodName, mi);
                    }
                }
            }

            if (callHandlers.ContainsKey(call.MethodName))
            {
                var attributes = callHandlers[call.MethodName].GetCustomAttributes(typeof(TrustLevelAttribute), true);

                //get the trust level from the method's TrustLevel attribute
                TrustLevelAttribute trustLevel;
                if (attributes.Any())
                    trustLevel = (TrustLevelAttribute)attributes.First();
                else
                    //if no attribute could be found, fall back to the default (highest possible trustlevel)
                    trustLevel = TrustLevelAttribute.MAX;

                //check if the sender is authorized to make that method call
                if (trustLevel.RequiredTrustLevel > this.TrustLevel)
                {
                    return new ErrorResult(RemoteErrorCode.NotAuthorizedError);
                }
                else
                {
                    //invoke the call handler method and return it's result
                    try
                    {
                        return (CallResult)callHandlers[call.MethodName].Invoke(component, call.Parameters.ToArray<object>());
                    }
                    catch (Exception e)
                    {
                        if (e.GetType() == typeof(ArgumentException) || e.GetType() == typeof(TargetParameterCountException))
                        {
                            return new ErrorResult(RemoteErrorCode.InvalidParametersError);
                        }
                        else { throw; }
                    }
                }
            }
            //Invoked method could not be found => return ErrorResult
            else
            {
                return new ErrorResult(RemoteErrorCode.MethodNotFoundError);

            }
        }
        public void TestRemoteMethodCallSerialization_NoParameters()
        {
            var call = new RemoteMethodCall();

            call.ComponentName = "component";
            call.CallId = Guid.NewGuid();
            call.MethodName = "sampleMethod";

            var xml = call.Serialize();

            var roundtrip = new RemoteMethodCall();
            Assert.IsTrue(roundtrip.Validate(xml));
            roundtrip.Deserialize(xml);

            Assert.IsNotNull(roundtrip);
            Assert.AreEqual<Guid>(call.CallId, roundtrip.CallId);
            Assert.AreEqual<string>(call.ComponentName, roundtrip.ComponentName);
            Assert.AreEqual<string>(call.MethodName, roundtrip.MethodName);
        }