Esempio n. 1
0
		public CommandRouter(RequestHandlerDirectory requestHandlers)
		{
			if (requestHandlers == null)
			{
				throw new ArgumentNullException("requestHandlers");
			}

			if (requestHandlers.Count == 0)
			{
				throw new ArgumentException("No request handlers were found in the directory.", "requestHandlers");
			}

			foreach (RequestHandlerMapping mapping in requestHandlers)
			{
				var handlerInfo = new RequestHandlerInfo() { RequestHandlerType = mapping.RequestHandlerType, ReentrantInstance = null };

				try
				{
					_HandlerDirectory.Add(mapping.Command, handlerInfo);
				}
				catch (ArgumentException)
				{
					throw new ArgumentException("The same command was mapped to more than one request handler.", "requestHandlers");
				}
			}
		}
Esempio n. 2
0
        public IRequestHandler MapHandler(string url)
        {
            RequestHandlerInfo handlerInfo = null;

            if (!_HandlerDirectory.TryGetValue(ParseCommandFromUrl(url), out handlerInfo))
            {
                return(null);
            }

            if (handlerInfo == null)
            {
                return(null);
            }

            if (handlerInfo.ReentrantInstance != null)
            {
                return(handlerInfo.ReentrantInstance);
            }

            IRequestHandler handler = (IRequestHandler)Activator.CreateInstance(handlerInfo.RequestHandlerType);

            if (handler != null && handler.IsReentrant)
            {
                handlerInfo.ReentrantInstance = handler;
            }

            return(handler);
        }
Esempio n. 3
0
        public CommandRouter(RequestHandlerDirectory requestHandlers)
        {
            if (requestHandlers == null)
            {
                throw new ArgumentNullException("requestHandlers");
            }

            if (requestHandlers.Count == 0)
            {
                throw new ArgumentException("No request handlers were found in the directory.", "requestHandlers");
            }

            foreach (RequestHandlerMapping mapping in requestHandlers)
            {
                var handlerInfo = new RequestHandlerInfo()
                {
                    RequestHandlerType = mapping.RequestHandlerType, ReentrantInstance = null
                };

                try
                {
                    _HandlerDirectory.Add(mapping.Command, handlerInfo);
                }
                catch (ArgumentException)
                {
                    throw new ArgumentException("The same command was mapped to more than one request handler.", "requestHandlers");
                }
            }
        }
Esempio n. 4
0
        public void Receive(Message message)
        {
            // Each time a message is received, we will scan the list of pending exchanges
            // to see whether or not any of them have timed out, and remove them.
            DateTime now = DateTime.Now;

            foreach (uint correlationId in mPendingExchangesMap.Keys.ToList())
            {
                Exchange exchange = mPendingExchangesMap[correlationId];
                if ((now - exchange.Requested).TotalMilliseconds > exchange.Timeout)
                {
                    mPendingExchangesMap.Remove(correlationId);
                    exchange.SetTimedOut();
                }
            }

            message.Received = now;

            /* Note: The following code is a bit heavy on the use of reflection.
             * This is to work around my inability to statically type the references
             * to the generic exchanges. I am open to suggestions on how to do this in a
             * way more conducive to static typing */

            // There are two situations that can occur when a message is received.
            // (1) The message is a request to do something
            // (2) The message is a response to a request that we made (i.e. completing a pending exchange)
            if (message.CorrelationId > 0)
            {
                // This message is (supposedly) a response to a request we made
                if (mPendingExchangesMap.TryGetValue(message.CorrelationId, out Exchange exchange))
                {
                    // Just in case, if this exchange is already completed, remove it and early exit
                    if (exchange.Complete)
                    {
                        mPendingExchangesMap.Remove(message.CorrelationId);
                        return;
                    }

                    // Get property infos via reflection
                    Type exchangeType     = exchange.GetType();
                    Type exchangeDataType = exchangeType.GetGenericArguments()[0];

                    exchange.Complete  = true;
                    exchange.Success   = message.Success;
                    exchange.Details   = message.Details;
                    exchange.Completed = now;

                    // Deserializing JSON
                    // If we failed to deserialize JSON, we fail to receive the response to this exchange
                    object data = null;
                    if (!exchangeDataType.Equals(None.NoneType))
                    {
                        try {
                            if (string.IsNullOrWhiteSpace(message.Data))
                            {
                                data = null;
                            }
                            else
                            {
                                data = JsonConvert.DeserializeObject(message.Data, exchangeDataType);
                            }
                        }
                        catch (JsonException)
                        {
                            exchange.Success = false;
                            exchange.Details = $"Failed to deserialize incoming response data. " +
                                               $"Expected to receive a {exchangeDataType.Name}, but received an object that could not be deserialized into this type. " +
                                               $"Are you asking for the correct type?";
                        }
                    }

                    // Succeeded in deserializing, now set data
                    if (data != null)
                    {
                        exchange.Property_Data.SetValue(exchange, data);
                    }

                    // Remove this response from the pending exchanges map
                    mPendingExchangesMap.Remove(message.CorrelationId);
                }
            }
            else
            {
                // This message is (supposedly) a request
                if (mRequestHandlersMap.TryGetValue(message.Request?.ToLower(), out List <RequestHandlerInfo> list))
                {
                    for (int i = 0; i < list.Count(); i++)
                    {
                        RequestHandlerInfo requestHandlerInfo = list[i];

                        // Does this request really need deserialized data? If so, let's get it.
                        // Once again, if we fail to deserialize, we fail to receive
                        object requestData = null;
                        if (!requestHandlerInfo.RequestType.Equals(None.NoneType))
                        {
                            try {
                                if (string.IsNullOrWhiteSpace(message.Data))
                                {
                                    requestData = null;
                                }
                                else
                                {
                                    requestData = JsonConvert.DeserializeObject(message.Data, requestHandlerInfo.RequestType);
                                }
                            }
                            catch (JsonException) { continue; }
                        }

                        // Create the request object to be given to the handler invoker
                        Type    requestType = Request.GenericType.MakeGenericType(requestHandlerInfo.RequestType);
                        Request request     = Activator.CreateInstance(requestType) as Request;
                        request.SenderName = message.SenderName;
                        request.SenderId   = message.SenderId;
                        request.Sent       = message.Sent;
                        request.Received   = message.Received ?? now;

                        // Give data to the request object if we need to
                        if (requestData != null)
                        {
                            request.Property_Data.SetValue(request, requestData);
                        }

                        // Create the response object to be given to the handler invoker
                        Type     responseType = Response.GenericType.MakeGenericType(requestHandlerInfo.ResponseType);
                        Response response     = Activator.CreateInstance(responseType) as Response;

                        // Invoke the delegate
                        requestHandlerInfo.Invoker.DynamicInvoke(request, response);

                        // Get the data after calling delegate
                        object responseData = null;
                        if (!requestHandlerInfo.ResponseType.Equals(None.NoneType))
                        {
                            responseData = response.Property_Data.GetValue(response);
                        }

                        Message responseMessage = new Message()
                        {
                            CorrelationId = message.Id,
                            DestinationId = message.SenderId,
                            Success       = response.Success,
                            Details       = response.Details,
                            Data          = JsonConvert.SerializeObject(responseData)
                        };

                        mConnectionBinder?.Invoke(responseMessage);
                    }
                }
                else
                {
                    // No such request found, respond back with this message

                    Message responseMessage = new Message()
                    {
                        CorrelationId = message.Id,
                        DestinationId = message.SenderId,
                        Success       = false,
                        Details       = $"No such request: '{message.Request}'",
                        Data          = null
                    };

                    mConnectionBinder?.Invoke(responseMessage);
                }
            }
        }
Esempio n. 5
0
        private void RegisterMethod(MethodInfo method, RequestHandlerAttribute attribute, object owner)
        {
            if (mRegisteredMethods.Contains(method))
            {
                return;
            }
            mRegisteredMethods.Add(method);

            // Verify request name
            if (string.IsNullOrWhiteSpace(attribute.RequestName) || !(RequestNameRegex.IsMatch(attribute.RequestName)))
            {
                ThrowInvalidMethodSignatureException(method, "has an invalid request name. Request names are case-insensitive, and must consist only of alphanumeric characters, hyphens and underscores");
            }

            // Verify request handler method signature
            ParameterInfo[] parameters = method.GetParameters();
            if (parameters.Length != 2)
            {
                ThrowInvalidMethodSignatureException(method, $"has an invalid method signature. Request handler methods must have only two parameters, {nameof(Request)} and {nameof(Response)}.");
            }
            if (IsRefInOut(parameters[0]) || IsRefInOut(parameters[0]))
            {
                ThrowInvalidMethodSignatureException(method, "has an invalid method signature. The parameters in a request handler method cannot be marked as in, out, or pass by reference.");
            }
            if (!IsGenericType(parameters[0].ParameterType, Request.GenericType) || !IsGenericType(parameters[1].ParameterType, Response.GenericType))
            {
                ThrowInvalidMethodSignatureException(method, $"has an invalid method signature. " +
                                                     $"The first parameter must be a {nameof(Request)}, whose generic type represents the type of data this request will receive. " +
                                                     $"The second parameter must be a {nameof(Response)}, whose generic type represents the type of data this request will respond with. " +
                                                     $"Either type parameter can be the {nameof(None)} type if the request handler should not receive or respond, respectively, with any data.");
            }
            if (!method.ReturnType.Equals(typeof(void)))
            {
                ThrowInvalidMethodSignatureException(method, "has an invalid method signature. Request handlers should not return anything and should have a void return type.");
            }

            Type requestType  = parameters[0].ParameterType.GetGenericArguments()[0];
            Type responseType = parameters[1].ParameterType.GetGenericArguments()[0];

            // Check that response data type is concrete
            if (!IsConcrete(responseType))
            {
                ThrowInvalidMethodSignatureException(method, $"has an invalid method signature. The response type parameter, {responseType}, is not a concrete class. Response type parameters must be concrete so that the deserializer knows what type to deserialize into.");
            }

            // Create delegate type
            Type delegateType = typeof(RequestHandler <,>).MakeGenericType(requestType, responseType);

            // Create delegate
            Delegate callback;

            if (owner == null)
            {
                callback = method.CreateDelegate(delegateType);
            }
            else
            {
                callback = method.CreateDelegate(delegateType, owner);
            }

            // Create request handler info
            RequestHandlerInfo requestHandlerInfo = new RequestHandlerInfo
            {
                Owner        = owner,
                Attr         = attribute,
                Invoker      = callback,
                RequestType  = requestType,
                ResponseType = responseType
            };

            if (!mRequestHandlersMap.TryGetValue(attribute.RequestName, out var list))
            {
                list = new List <RequestHandlerInfo>();
                mRequestHandlersMap[attribute.RequestName] = list;
            }

            // Add the request handler to the list and sort by reverse of priority levels
            // (High priority handlers at start of list, low priority handlers at end)
            list.Add(requestHandlerInfo);
            list.Sort((a, b) => - a.Attr.Priority.CompareTo(b.Attr.Priority));
        }