コード例 #1
0
        /// <summary>
        /// Serializes the message passed into a byte array.
        /// </summary>
        /// <param name="msg">The message to be serialized.</param>
        /// <param name="es">The enhanced stream where the output is to be written.</param>
        /// <returns>The size of the message written in bytes.</returns>
        /// <remarks>
        /// The buffer returned includes the preamble, the message type,
        /// and the payload.
        /// </remarks>
        public static int Save(EnhancedStream es, Msg msg)
        {
            string      typeID;
            int         cbMsg;
            MsgInfo     msgInfo;
            EnvelopeMsg envelopeMsg;
            int         startPos;

            // Write the type and payload first to determine the message
            // size and then go back and write the preamble.  Note that
            // for envelope messages, we're going to get the typeID from the
            // message instance, rather than looking it up in the type map.

            envelopeMsg = msg as EnvelopeMsg;
            if (envelopeMsg != null)
            {
                typeID = envelopeMsg.TypeID;
            }
            else
            {
                lock (syncLock)
                    typeMap.TryGetValue(msg.GetType(), out msgInfo);

                if (msgInfo == null)
                {
                    throw new MsgException("Unregistered message class [{0}].", msg.GetType().FullName);
                }

                typeID = msgInfo.TypeID;
            }

            startPos = (int)es.Position;

            es.Write(preamble, 0, preamble.Length); // Leave room for the preamble
            es.WriteString16(typeID);
#if DEBUG
            msg.writeBase = false;                  // $hack(jeff.lill): Strictly speaking, this isn't threadsafe
#endif
            msg.WriteBase(es);
#if DEBUG
            Assertion.Test(msg.writeBase, "Derived [Msg] classes must call [base.WriteBase()].");
#endif
            msg.WritePayload(es);
            cbMsg = (int)es.Position;

            es.Position = startPos;
            es.WriteByte(magic);                    // Magic number
            es.WriteByte(0);                        // Message format
            es.WriteInt32(cbMsg);                   // Total message length

            es.Position = startPos + cbMsg;
            return(cbMsg);
        }
コード例 #2
0
        /// <summary>
        /// Constructs a <see cref="MsgRequestContext" /> for transactions that are within a session.
        /// </summary>
        /// <param name="session">The <see cref="DuplexSession" />.</param>
        /// <param name="query">The request <see cref="Msg" />.</param>
        /// <exception cref="ArgumentException">Thrown if the message passed does not have all of the headers necessary to be a request.</exception>
        internal MsgRequestContext(DuplexSession session, Msg query)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }

            if (query._FromEP == null)
            {
                throw new ArgumentException("Message cannot be a request: Null [_FromEP] header.", "requestMsg");
            }

            if (query._SessionID == Guid.Empty)
            {
                throw new ArgumentException("Message cannot be a request: Empty [_SessionID] header.", "requestMsg");
            }

            this.Header = query._ExtensionHeaders[MsgHeaderID.DuplexSession];
            if (this.Header == null)
            {
                throw new ArgumentException("Message is not a DuplexSession query.", "requestMsg");
            }

            this.router    = session.Router;
            this.session   = session;
            this.FromEP    = query._FromEP.Clone();
            this.SessionID = query._SessionID;
#if TRACE
            this.TraceName = query.GetType().Name;
#else
            this.TraceName = "(trace disabled)";
#endif
        }
コード例 #3
0
        private bool closed = false;                        // True if the transaction has completed

        /// <summary>
        /// Constructs a <see cref="MsgRequestContext" /> for transactions that are not within a session.
        /// </summary>
        /// <param name="router">The <see cref="MsgRouter" />.</param>
        /// <param name="requestMsg">The request <see cref="Msg" />.</param>
        /// <exception cref="ArgumentException">Thrown if the message passed does not have all of the headers necessary to be a request.</exception>
        internal MsgRequestContext(MsgRouter router, Msg requestMsg)
        {
            if (router == null)
            {
                throw new ArgumentNullException("router");
            }

            if (requestMsg._FromEP == null)
            {
                throw new ArgumentException("Message cannot be a request: Null [_FromEP] header.", "requestMsg");
            }

            if (requestMsg._SessionID == Guid.Empty)
            {
                throw new ArgumentException("Message cannot be a request: Empty [_SessionID] header.", "requestMsg");
            }

            this.router    = router;
            this.session   = null;
            this.FromEP    = requestMsg._FromEP.Clone();
            this.SessionID = requestMsg._SessionID;
#if TRACE
            this.TraceName = requestMsg.GetType().Name;
#else
            this.TraceName = "(trace disabled)";
#endif
        }
コード例 #4
0
        /// <summary>
        /// Called to dispatch a server side session.
        /// </summary>
        /// <param name="msg">The message initiating the session.</param>
        /// <param name="target">The target object instance.</param>
        /// <param name="method">The target method information.</param>
        /// <param name="sessionInfo">The session information associated with the handler.</param>
        /// <remarks>
        /// The target and method parameter will specify the message handler
        /// for the message passed.
        /// </remarks>
        public void ServerDispatch(Msg msg, object target, MethodInfo method, SessionHandlerInfo sessionInfo)
        {
            ISession session;
            bool     start = false;

            Assertion.Test((msg._Flags & (MsgFlag.OpenSession & MsgFlag.ServerSession)) == (MsgFlag.OpenSession & MsgFlag.ServerSession));
            Assertion.Test(msg._SessionID != Guid.Empty);

            using (TimedLock.Lock(router.SyncRoot))
            {
                // Create a session with this ID if one doesn't already exist.

                serverSessions.TryGetValue(msg._SessionID, out session);

                if (session == null)
                {
                    if (sessionInfo.SessionType == null)
                    {
                        SysLog.LogError("Session creation failed for received [{0}] message: No session type specified in [MsgSession] tag for handler [{1}.{2}({3})}.",
                                        msg.GetType().FullName,
                                        target.GetType().FullName,
                                        method.Name,
                                        method.GetParameters()[0].ParameterType.Name);
                        return;
                    }

                    start   = true;
                    session = Helper.CreateInstance <ISession>(sessionInfo.SessionType);
                    session.InitServer(router, this, router.SessionTimeout, msg, target, method, sessionInfo);
                    serverSessions.Add(msg._SessionID, session);
                }
            }

            // Dispatch the message outside of the lock (to avoid deadlocks)

            if (start)
            {
                session.StartServer();
            }
            else
            {
                session.OnMsg(msg, sessionInfo);
            }
        }
コード例 #5
0
        // $todo(jeff.lill):
        //
        // I'm not really sure if we need this to be dispached
        // on a worker thread since MsgRouter.OnProcessMsg()
        // (who calls MsgDispatcher.Dispatch()) is already
        // running on a fresh worker thread.  We'd probably see
        // a performance boost by having Dispatch() call the
        // handler directly.  Something to look into when I
        // have more time.

        /// <summary>
        /// Handles the dispatch on a worker thread.
        /// </summary>
        /// <param name="state">The DispatchInfo.</param>
        private void OnDispatch(object state)
        {
            DispatchInfo       info        = (DispatchInfo)state;
            MsgHandler         handler     = info.Handler;
            Msg                msg         = info.Msg;
            SessionHandlerInfo sessionInfo = handler == null ? null : handler.SessionInfo;
            ISessionManager    sessionMgr;

            try
            {
                // If there's a router associated with this instance and the message
                // has a non-empty _SessionID then we'll either initiate a server side
                // session or simply route the message to the session, depending
                // on the MsgFlag.OpenSession bit.
                //
                // If neither of these conditions are true then route the message
                // directly to the handler.

                if (router != null && msg._SessionID != Guid.Empty)
                {
                    sessionMgr = router.SessionManager;
                    if ((msg._Flags & MsgFlag.OpenSession) != 0)
                    {
                        if (handler == null)
                        {
                            NetTrace.Write(MsgRouter.TraceSubsystem, 1, "Dispatch: Message Discarded", "No message handler for: " + msg.GetType().Name, string.Empty);
                            return;
                        }

                        sessionMgr.ServerDispatch(msg, handler.Target, handler.Method, sessionInfo);
                    }
                    else
                    {
                        sessionMgr.OnMsg(msg, sessionInfo);
                    }
                }
                else
                {
                    handler.Method.Invoke(handler.Target, info.Args);
                }
            }
            catch (Exception e)
            {
                NetTrace.Write(MsgRouter.TraceSubsystem, 0, "App Exception", e);
                SysLog.LogException(e);
            }
        }
コード例 #6
0
        /// <summary>
        /// Dispatches the message passed to the appropriate handler in
        /// the associated target instance.  Note that the dispatch call will
        /// be performed on a worker thread.
        /// </summary>
        /// <param name="msg">The message to be dispatched.</param>
        /// <returns><c>true</c> if the message was successfully dispatched.</returns>
        /// <remarks>
        /// <para>
        /// This method uses two basic implementation techniques depending on whether
        /// the message's target endpoint is physical or logical.
        /// </para>
        /// <para><b><u>Physical Endpoint Implementation</u></b></para>
        /// <para>
        /// For logical endpoint handlers, MsgDispatcher maintains a hash table
        /// mapping each handler method to the method's message parameter type.
        /// When the router receives a message targeted at a physical endpoint,
        /// the router calls <see cref="Dispatch" /> which then calls the handler
        /// method whose parameter type matches that of the message being dispatched.
        /// <see cref="Dispatch" /> will return true in this case.
        /// </para>
        /// <para>
        /// If no match is found and a method is tagged with <c>[MsgHandler(Default=true)]</c>
        /// then the message will be dispatched there.  If there's no default
        /// handler, then the message will be dropped and <see cref="Dispatch" />
        /// will return false.
        /// </para>
        /// <para>
        /// The message's <see cref="MsgFlag">MsgFlag.Broadcast</see> flag is ignored when dispatching
        /// messages targeted at physical endpoints.
        /// </para>
        /// <para><b><u>Logical Endpoint Implementation</u></b></para>
        /// <para>
        /// The logical endpoint implementation is a bit trickier.  MsgDispatcher
        /// includes a LogicalRouteTable instance of the logical endpoints it
        /// collects from [MsgHandler] attributes.  For each distinct logical
        /// endpoint, the class maintains a hash table mapping the message type
        /// of each tagged message handler method to the method instance.
        /// These hash tables are stored in the <see cref="LogicalRoute.Handlers" /> property
        /// of the routes stored in the LogicalRouteTable.
        /// </para>
        /// <para>
        /// When the router receives a message targeted at a logical endpoint,
        /// the router call <see cref="Dispatch" />, which will then
        /// search the logical route table for one or more matching logical routes.
        /// If any are found, then <see cref="Dispatch" /> will randomly select one and then examine
        /// the hash table saved in the route's <see cref="LogicalRoute.Handlers" /> property looking
        /// for a method handler whose parameter type matches the message type
        /// received, calling the handler if a match is found and <see cref="Dispatch" />
        /// will return false.
        /// </para>
        /// <para>
        /// If no match is found and a method is tagged with <c>[MsgHandler(Default=true)]</c>
        /// then the message will be dispatched there.  If there's no default
        /// handler, then the message will not be dispatched and <see cref="Dispatch" />
        /// will return false.
        /// </para>
        /// <para>
        /// <see cref="Dispatch" /> works a bit differently for messages targeted at
        /// logical endpoints if the <see cref="MsgFlag">MsgFlag.Broadcast</see> flag is set.
        /// In this situation, instead of randomly selecting a single matching
        /// route and dispatching the message there, <see cref="Dispatch" /> dispatch the
        /// message to all matching routes.  Note though, that only one message
        /// handler associated with each route will be called (based on the
        /// type of the message).
        /// </para>
        /// </remarks>
        public bool Dispatch(Msg msg)
        {
            // Look up the approriate handler for this message.

            MsgHandler handler = null;

            if (msg._ToEP == null || msg._ToEP.IsPhysical)
            {
                // We don't dispatch EnvelopeMsgs.

                if (msg is EnvelopeMsg)
                {
                    return(false);
                }

                // Get the physical handler

                using (TimedLock.Lock(syncLock))
                {
                    physHandlers.TryGetValue(msg.GetType(), out handler);

                    if (handler == null)
                    {
                        handler = defPhysHandler;
                    }
                }

                // If we couldn't find a handler for this message type and the
                // message isn't associated with a session then discard
                // the message.  Messages with no handler that are associated
                // with a session will be routed to the session instance
                // to be handled there.

                if (handler == null && msg._SessionID == Guid.Empty)
                {
                    if (router.IsOpen)
                    {
                        NetTrace.Write(MsgRouter.TraceSubsystem, 1, "Dispatch Physical", router.GetType().Name + ": " + msg.GetType().Name + " router=" + router.RouterEP.ToString(), " No handler");
                    }

                    return(false);
                }

                if (router.IsOpen)
                {
                    NetTrace.Write(MsgRouter.TraceSubsystem, 1, "Dispatch Physical", router.GetType().Name + ": " + msg.GetType().Name + " router=" + router.RouterEP.ToString(), " Target: " + (handler == null ? "(none)" : handler.Target.GetType().Name));
                }
            }
            else if ((msg._Flags & MsgFlag.Broadcast) != 0)
            {
                // A broadcast message is being targeted at a logical endpoint.

                List <LogicalRoute> routes;
                LogicalRoute        route;

                using (TimedLock.Lock(syncLock))
                {
                    routes = logicalRoutes.GetRoutes(msg._ToEP);
                    if (routes.Count == 0)
                    {
                        return(false);
                    }
                }

                for (int i = 0; i < routes.Count; i++)
                {
                    route   = routes[i];
                    handler = null;

                    if (!route.Handlers.TryGetValue(msg.GetType().FullName, out handler))
                    {
                        route.Handlers.TryGetValue(DefaultHandler, out handler);
                    }

                    if (handler == null)
                    {
                        continue;
                    }

                    NetTrace.Write(MsgRouter.TraceSubsystem, 1, "Dispatch Logical", msg.GetType().Name + "[" + msg._ToEP + "]", "Target: " + (handler == null ? "(none)" : handler.Target.GetType().Name));

                    // Verify that the message type is actually compatible with the
                    // handler's message parameter.

                    if (!handler.MsgType.IsAssignableFrom(msg.GetType()))
                    {
                        SysLog.LogWarning("Unable to dispatch message type [{0}] to [{0}.{1}()] because the handler parameter type is not compatible.",
                                          msg.GetType(), handler.Target.GetType().FullName, handler.Method.Name);
                        return(false);
                    }

                    if ((msg._Flags & MsgFlag.Priority) != 0)
                    {
                        router.ThreadPool.QueuePriorityTask(onDispatch, new DispatchInfo(msg, handler, new object[] { msg }));
                    }
                    else
                    {
                        router.ThreadPool.QueueTask(onDispatch, new DispatchInfo(msg, handler, new object[] { msg }));
                    }
                }

                return(true);
            }
            else
            {
                // A non-broadcast message is being targeted at a logical endpoint.

                List <LogicalRoute> routes;
                LogicalRoute        route;

                Assertion.Test((msg._Flags & MsgFlag.Broadcast) == 0);
                Assertion.Test(msg._ToEP.IsLogical);

                using (TimedLock.Lock(syncLock))
                {
                    routes = logicalRoutes.GetRoutes(msg._ToEP);
                    if (routes.Count == 0)
                    {
                        return(false);
                    }

                    route   = routes[Helper.RandIndex(routes.Count)];
                    handler = null;

                    if (!route.Handlers.TryGetValue(msg.GetType().FullName, out handler))
                    {
                        route.Handlers.TryGetValue(DefaultHandler, out handler);
                    }
                }

                // If we couldn't find a handler for this message type and the
                // message isn't associated with a session then discard
                // the message.  Messages with no handler that are associated
                // with a session will be routed to the session instance
                // to be handled there.

                if (handler == null && msg._SessionID == Guid.Empty)
                {
                    NetTrace.Write(MsgRouter.TraceSubsystem, 1, "Dispatch Logical", msg.GetType().Name, "No handler");
                    return(false);
                }

                NetTrace.Write(MsgRouter.TraceSubsystem, 1, "Dispatch Logical", msg.GetType().Name + "[" + msg._ToEP + "]", "Target: " + (handler == null ? "(none)" : handler.Target.GetType().Name));
            }

            // Verify that the message type is actually compatible with the
            // handler's message parameter.

            if (handler != null && !handler.MsgType.IsAssignableFrom(msg.GetType()))
            {
                SysLog.LogWarning("Unable to dispatch message type [{0}] to [{0}.{1}()] because the handler parameter type is not compatible.",
                                  msg.GetType(), handler.Target.GetType().FullName, handler.Method.Name);
                return(false);
            }

            if ((msg._Flags & MsgFlag.Priority) != 0)
            {
                router.ThreadPool.QueuePriorityTask(onDispatch, new DispatchInfo(msg, handler, new object[] { msg }));
            }
            else
            {
                router.ThreadPool.QueueTask(onDispatch, new DispatchInfo(msg, handler, new object[] { msg }));
            }

            return(true);
        }