예제 #1
0
        /// <summary>
        /// Called by the router whenever it receives a message with a non-empty
        /// _SessionID.  This method dispatches the message to the associated session
        /// (if any).
        /// </summary>
        /// <param name="msg">The message.</param>
        /// <param name="sessionInfo">The session information associated with the handler.</param>
        public void OnMsg(Msg msg, SessionHandlerInfo sessionInfo)
        {
            ISession session;

            Assertion.Test(msg._SessionID != Guid.Empty);

            using (TimedLock.Lock(router.SyncRoot))
            {
                if ((msg._Flags & MsgFlag.ServerSession) != 0)
                {
                    serverSessions.TryGetValue(msg._SessionID, out session);
                }
                else
                {
                    clientSessions.TryGetValue(msg._SessionID, out session);
                }
            }

            if (session != null)
            {
                msg._Trace(router, 2, "SessionManager: Dispached", null);
                session.OnMsg(msg, sessionInfo);
            }
            else
            {
                msg._Trace(router, 2, "SessionManager: No session", null);
            }
        }
예제 #2
0
        /// <summary>
        /// Initializes a server side session.
        /// </summary>
        /// <param name="router">The associated message router.</param>
        /// <param name="sessionMgr">The associated session manager.</param>
        /// <param name="ttl">Session time-to-live.</param>
        /// <param name="msg">The message that triggered this session.</param>
        /// <param name="target">The dispatch target instance.</param>
        /// <param name="method">The dispatch method.</param>
        /// <param name="sessionInfo">
        /// The session information associated with the handler or <c>null</c>
        /// to use session defaults.
        /// </param>
        public virtual void InitServer(MsgRouter router, ISessionManager sessionMgr, TimeSpan ttl, Msg msg, object target,
                                       MethodInfo method, SessionHandlerInfo sessionInfo)
        {
            if (sessionInfo == null)
            {
                sessionInfo = SessionHandlerInfo.Default;
            }

            this.isClient      = false;
            this.router        = router;
            this.sessionMgr    = sessionMgr;
            this.sessionID     = msg._SessionID;
            this.isAsync       = sessionInfo.IsAsync;
            this.isRunning     = true;
            this.startTime     = SysTime.Now;
            this.finishTime    = DateTime.MaxValue;
            this.ttd           = startTime + ttl;
            this.ttl           = ttl;
            this.serverInitMsg = msg;
            this.clientEP      = msg._FromEP;
            this.target        = target;
            this.method        = method;
            this.sessionInfo   = sessionInfo != null ? sessionInfo : defSessionInfo;
            this.cachedReply   = null;

            msg._Session = (ISession)this;
        }
예제 #3
0
        /// <summary>
        /// Static constructor.
        /// </summary>
        static SessionBase()
        {
            MsgSessionAttribute attr;

            attr           = new MsgSessionAttribute();
            attr.Type      = SessionTypeID.Unknown;
            defSessionInfo = new SessionHandlerInfo(attr);
        }
예제 #4
0
        //---------------------------------------------------------------------
        // Common implementation members

        /// <summary>
        /// Handles any received messages (not including the first message directed to the server)
        /// associated with this session.
        /// </summary>
        /// <param name="msg">The message.</param>
        /// <param name="sessionInfo">The session information associated with the handler.</param>
        /// <remarks>
        /// <note>
        /// The first message sent to server side session will result in a
        /// call to <see cref="StartServer" /> rather than a call to this method.
        /// </note>
        /// </remarks>
        public override void OnMsg(Msg msg, SessionHandlerInfo sessionInfo)
        {
            if (base.IsClient)
            {
                OnClientMsg(msg);
            }
            else
            {
                OnServerMsg(msg);
            }
        }
예제 #5
0
        /// <summary>
        /// Constructs a MsgHandler instance mapped to a reflected
        /// message handler method.
        /// </summary>
        /// <param name="target">The target object instance.</param>
        /// <param name="method">Information about the instance method that will actually handle the message.</param>
        /// <param name="msgType">The message type accepted by the handler.</param>
        /// <param name="handlerAttr">The [MsgHandler] instance for the handler (or <c>null</c>).</param>
        /// <param name="sessionInfo">The session information for the handler (or <c>null</c>).</param>
        public MsgHandler(object target, MethodInfo method, System.Type msgType, MsgHandlerAttribute handlerAttr, SessionHandlerInfo sessionInfo)
        {
            if (sessionInfo == null)
            {
                sessionInfo = SessionHandlerInfo.Default;
            }

            this.Target       = target;
            this.Method       = method;
            this.MsgType      = msgType;
            this.DynamicScope = handlerAttr == null ? null : handlerAttr.DynamicScope;
            this.SessionInfo  = sessionInfo;
        }
예제 #6
0
        /// <summary>
        /// Constructs a MsgHandler instance mapped to a reflected
        /// message handler method.
        /// </summary>
        /// <param name="target">The target object instance.</param>
        /// <param name="method">Information about the instance method that will actually handle the message.</param>
        /// <param name="msgType">The message type accepted by the handler.</param>
        /// <param name="handlerAttr">The [MsgHandler] instance for the handler (or <c>null</c>).</param>
        /// <param name="sessionAttr">The [MsgSession] instance for the handler (or <c>null</c>).</param>
        public MsgHandler(object target, MethodInfo method, System.Type msgType, MsgHandlerAttribute handlerAttr, MsgSessionAttribute sessionAttr)
        {
            this.Target       = target;
            this.Method       = method;
            this.MsgType      = msgType;
            this.DynamicScope = handlerAttr == null ? null : handlerAttr.DynamicScope;

            if (sessionAttr == null)
            {
                this.SessionInfo = SessionHandlerInfo.Default;
            }
            else
            {
                this.SessionInfo = new SessionHandlerInfo(sessionAttr);
            }
        }
예제 #7
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);
            }
        }
예제 #8
0
 /// <summary>
 /// Initializes a client side session.
 /// </summary>
 /// <param name="router">The associated message router.</param>
 /// <param name="sessionMgr">The associated session manager.</param>
 /// <param name="ttl">Session time-to-live.</param>
 /// <param name="sessionID">The session ID to assign to this session</param>
 public virtual void InitClient(MsgRouter router, ISessionManager sessionMgr, TimeSpan ttl, Guid sessionID)
 {
     this.isClient      = true;
     this.router        = router;
     this.sessionMgr    = sessionMgr;
     this.sessionID     = sessionID;
     this.startTime     = SysTime.Now;
     this.finishTime    = DateTime.MaxValue;
     this.ttd           = startTime + ttl;
     this.ttl           = ttl;
     this.serverInitMsg = null;
     this.clientEP      = null;
     this.target        = null;
     this.method        = null;
     this.sessionInfo   = null;
     this.cachedReply   = null;
 }
예제 #9
0
        /// <summary>
        /// Adds an explicit physical message handler to the dispatcher.
        /// </summary>
        /// <param name="callback">The handler callback.</param>
        /// <param name="msgType">The message type to be associated with the handler.</param>
        /// <param name="sessionInfo">
        /// The session related information to be related to this handler (or <c>null</c>).
        /// </param>
        /// <remarks>
        /// <note>
        /// Methods registered as handlers via this method will have to declare
        /// their message parameter as the base Msg class so that the compiler can
        /// create MsgHandlerDelegate instances.  The message parameter can then be
        /// cast to the proper message type within the handler method.
        /// </note>
        /// </remarks>
        public void AddPhysical(MsgHandlerDelegate callback, System.Type msgType, SessionHandlerInfo sessionInfo)
        {
            MethodInfo method;
            MsgHandler handler;

            method  = callback.Method;
            handler = new MsgHandler(callback.Target, method, msgType, null, sessionInfo);

            using (TimedLock.Lock(syncLock))
            {
                if (physHandlers.ContainsKey(msgType))
                {
                    throw new MsgException("Multiple physical handlers defined for message type [{0}].", msgType);
                }

                physHandlers.Add(msgType, handler);
            }
        }
예제 #10
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);
            }
        }
예제 #11
0
 /// <summary>
 /// Handles any received messages associated with this session.
 /// </summary>
 /// <param name="msg">The message.</param>
 /// <param name="sessionInfo">The session information associated with the handler.</param>
 public virtual void OnMsg(Msg msg, SessionHandlerInfo sessionInfo)
 {
 }
예제 #12
0
        /// <summary>
        /// Scans the target object passed for dispatchable methods and adds
        /// them to the dispatcher, using the <see cref="IDynamicEPMunger" />
        /// instances passed in the <paramref name="mungers"/> set to dynamically
        /// modify endpoints for message handlers marked with <c>[MsgHandler(DynamicScope="scope-name")]</c>.
        /// </summary>
        /// <param name="target">The object to be scanned.</param>
        /// <param name="mungers">
        /// The collection of <see cref="ScopeMunger" /> instances that specify
        /// zero or more endpoint mungers.  You may also pass <c>null</c>.
        /// </param>
        /// <param name="targetGroup">Optional dispatch target grouping instance (or <c>null</c>).</param>
        /// <remarks>
        /// <para>
        /// Calling this method will result in the GUID being updated and
        /// then call the associated router's <see cref="MsgRouter.OnLogicalEndpointSetChange"/>
        /// method.
        /// </para>
        /// <para>
        /// The <paramref name="targetGroup" /> parameter can be used to group
        /// together message dispatch handlers implemented by a different
        /// target object instances.  This functionality is important when
        /// the impleentation of message type specific handlers is spread
        /// across multiple target classes.
        /// </para>
        /// <para>
        /// An example of this use is how the <see cref="ClusterMember" />
        /// class' <see cref="ClusterMember.AddTarget" /> method to group the
        /// new target's message handler with the <see cref="ClusterMember" />
        /// handlers since they'll share the same logical endpoint.
        /// </para>
        /// <para>
        /// If <paramref name="targetGroup" /> is passed as <c>null</c> then
        /// separate routes will be maintained for each target instance resulting
        /// in messages be load balanced randomly across the instances.
        /// </para>
        /// </remarks>
        public void AddTarget(object target, IEnumerable <ScopeMunger> mungers, object targetGroup)
        {
            const string msgBadMethodSignature = "Illegal [MsgHandler] method signature for [{0}.{1}()]: {2}.";

            var scopeToMunger = new Dictionary <string, IDynamicEPMunger>(StringComparer.OrdinalIgnoreCase);

            System.Type  targetType = target.GetType();
            System.Type  msgType;
            MethodInfo[] methods;
            MsgHandler   duplicate;

            ParameterInfo[]    args;
            object[]           handlerAttrs;
            object[]           sessionAttrs;
            SessionHandlerInfo sessionInfo;
            bool fNewID;

            if (mungers != null)
            {
                foreach (var item in mungers)
                {
                    scopeToMunger[item.DynamicScope] = item.Munger;
                }
            }

            using (TimedLock.Lock(syncLock))
            {
                fNewID  = false;
                methods = targetType.GetMethods(BindingFlags.Instance | BindingFlags.Public);

                foreach (MethodInfo method in methods)
                {
                    // Ignore methods not tagged with [MsgHandler].

                    handlerAttrs = method.GetCustomAttributes(typeof(MsgHandlerAttribute), false);
                    if (handlerAttrs.Length == 0)
                    {
                        continue;
                    }

                    // Get the handler's session information (if any).

                    sessionAttrs = method.GetCustomAttributes(typeof(MsgSessionAttribute), false);
                    if (sessionAttrs.Length == 0)
                    {
                        sessionInfo = null;
                    }
                    else
                    {
                        sessionInfo = new SessionHandlerInfo((MsgSessionAttribute)sessionAttrs[0]);
                    }

                    // Validate the method signature: void OnMsg(Msg msg);

                    args = method.GetParameters();

                    if (method.ReturnType.UnderlyingSystemType != typeof(void))
                    {
                        throw new MsgException(msgBadMethodSignature, targetType.Name, method.Name, "Must return void.");
                    }

                    if (method.ReturnType.UnderlyingSystemType != typeof(void))
                    {
                        throw new MsgException(msgBadMethodSignature, targetType.Name, method.Name, "Must return void.");
                    }

                    if (args.Length != 1)
                    {
                        throw new MsgException(msgBadMethodSignature, targetType.Name, method.Name, "Must accept a single parameter.");
                    }

                    if (!args[0].ParameterType.IsDerivedFrom(typeof(Msg)))
                    {
                        throw new MsgException(msgBadMethodSignature, targetType.Name, method.Name, "Parameter must derive from Msg.");
                    }

                    foreach (MsgHandlerAttribute attr in handlerAttrs)
                    {
                        if (attr.IsPhysicalHandler)
                        {
                            // Deal with [MsgHandler(Default=true)] message handlers

                            if (attr.Default)
                            {
                                if (defPhysHandler != null)
                                {
                                    if (defPhysHandler.Target == target && defPhysHandler.Method == method)
                                    {
                                        continue;   // Looks like we reflected the same method twice
                                    }
                                    throw new MsgException("Multiple physical default message handlers: [{0}.{1}()] and [{2}.{3}()].",
                                                           defPhysHandler.Target.GetType().Name, defPhysHandler.Method.Name, targetType.Name, method.Name);
                                }

                                defPhysHandler = new MsgHandler(target, method, args[0].ParameterType, null, sessionInfo);
                                continue;
                            }

                            // Add the handler to the map, making sure that there's only one
                            // handler per message type

                            msgType = args[0].ParameterType;
                            physHandlers.TryGetValue(msgType, out duplicate);

                            if (duplicate != null)
                            {
                                if (duplicate.Target == target && duplicate.Method == method)
                                {
                                    continue;   // Looks like we reflected the same method twice
                                }
                                throw new MsgException("Multiple physical handlers defined for message type [{0}]: [{1}{2}()] and [{1}{3}()].",
                                                       msgType.Name, targetType.Name, duplicate.Method.Name, method.Name);
                            }

                            physHandlers[msgType] = new MsgHandler(target, method, msgType, null, sessionInfo);
                        }
                        else
                        {
                            // This is a logical endpoint handler.

                            string     paramType = attr.Default ? DefaultHandler : args[0].ParameterType.FullName;
                            MsgHandler handler   = new MsgHandler(target, method, args[0].ParameterType, attr, sessionInfo);
                            MsgEP      handlerEP;

                            handlerEP = attr.LogicalEP;
                            if (attr.DynamicScope != null)
                            {
                                IDynamicEPMunger munger;

                                if (scopeToMunger.TryGetValue(attr.DynamicScope, out munger))
                                {
                                    handlerEP = munger.Munge(attr.LogicalEP, handler);
                                }
                                else
                                {
                                    continue;   // Ignore this message handler
                                }
                            }

                            if (!logicalRoutes.Add(new LogicalRoute(handlerEP, paramType, handler), paramType, targetGroup))
                            {
                                if (attr.Default)
                                {
                                    throw new MsgException("A default logical handler is already defined for [{0}].", attr.LogicalEP);
                                }
                                else
                                {
                                    throw new MsgException("A logical message handler is already defined for endpoint [{0}] and message type [{1}].", attr.LogicalEP, paramType);
                                }
                            }

                            if (!fNewID)
                            {
                                fNewID = true;
                                logicalEndpointSetID = Helper.NewGuid();
                            }
                        }
                    }
                }
            }

            if (fNewID && router != null)
            {
                router.OnLogicalEndpointSetChange(logicalEndpointSetID);
            }
        }
예제 #13
0
        /// <summary>
        /// Adds an explicit logical endpoint message handler to the dispatcher.
        /// </summary>
        /// <param name="callback">The handler callback.</param>
        /// <param name="logicalEP">The logical endpoint.</param>
        /// <param name="msgType">The message type to be associated with the handler.</param>
        /// <param name="defHandler"><c>true</c> if this is the default handler for the endpoint.</param>
        /// <param name="sessionInfo">
        /// The session related information to be related to this handler (or <c>null</c>).
        /// </param>
        /// <param name="suppressAdvertise"><c>true</c> if the advertise messages should be suppressed for this endpoint.</param>
        /// <remarks>
        /// <para>
        /// Pass suppressAdvertise=<c>true</c> if the dispatcher and associated router to suppress
        /// the transmission of advertise messages to the other routers on the network.
        /// This is useful when multiple logical handlers are being added.  Once the handlers
        /// have been added, <see cref="LogicalAdvertise"/> can be called to force the
        /// advertise.
        /// </para>
        /// <note>
        /// Methods registered as handlers via this method will have to declare
        /// their message parameter as the base <see cref="Msg" /> class so that the compiler can
        /// create <see cref="MsgHandlerDelegate" /> instances.  The message parameter can then be
        /// cast to the proper message type within the handler method.
        /// </note>
        /// </remarks>
        public void AddLogical(MsgHandlerDelegate callback, MsgEP logicalEP, System.Type msgType, bool defHandler, SessionHandlerInfo sessionInfo, bool suppressAdvertise)
        {
            string     msgTypeName;
            string     key;
            MethodInfo method;
            MsgHandler handler;

            method      = callback.Method;
            msgTypeName = msgType.FullName;
            key         = defHandler ? DefaultHandler : msgTypeName;
            handler     = new MsgHandler(callback.Target, method, msgType, null, sessionInfo);

            using (TimedLock.Lock(syncLock))
            {
                if (!logicalRoutes.Add(new LogicalRoute(logicalEP, key, new MsgHandler(callback.Target, method, msgType, null, sessionInfo)), key))
                {
                    if (defHandler)
                    {
                        throw new MsgException("A default logical handler is already defined for [{0}].", logicalEP);
                    }
                    else
                    {
                        throw new MsgException("A logical message handler is alreay defined for endpoint [{0}] and message type [{1}].", logicalEP, msgType);
                    }
                }

                if (!suppressAdvertise)
                {
                    logicalEndpointSetID = Helper.NewGuid();
                }
            }

            if (!suppressAdvertise && router != null)
            {
                router.OnLogicalEndpointSetChange(logicalEndpointSetID);
            }
        }
예제 #14
0
 /// <summary>
 /// Adds a, explicit logical endpoint message handler to the dispatcher.
 /// </summary>
 /// <param name="callback">The handler callback.</param>
 /// <param name="logicalEP">The logical endpoint.</param>
 /// <param name="msgType">The message type to be associated with the handler.</param>
 /// <param name="defHandler"><c>true</c> if this is the default handler for the endpoint.</param>
 /// <param name="sessionInfo">
 /// The session related information to be related to this handler (or <c>null</c>).
 /// </param>
 /// <remarks>
 /// <note>
 /// Methods registered as handlers via this method will have to declare
 /// their message parameter as the base Msg class so that the compiler can
 /// create MsgHandlerDelegate instances.  The message parameter can then be
 /// cast to the proper message type within the handler method.
 /// </note>
 /// </remarks>
 public void AddLogical(MsgHandlerDelegate callback, MsgEP logicalEP, System.Type msgType, bool defHandler, SessionHandlerInfo sessionInfo)
 {
     AddLogical(callback, logicalEP, msgType, defHandler, sessionInfo, false);
 }