JsonCommandAssembly tests

May 17, 2011 at 9:52 PM
Edited May 17, 2011 at 9:59 PM

I was trying to get JsonCommandAssembly working within a unit test using the WebSocketClient - looking for a little help getting that working.  I was trying the simplest way I could see before working on the javascript/jquery code from the browser and/or potentially a winform client connecting to the service.

I think I'm just missing something obvious or basically wrong here - I'm expecting to the client send a message getting to the CHAT method in JsonCommandAssembly, but it is never hit?  Will the WebSockClient send the sub-command message?

To test, I setup a new test project, using the JsonCommandAssembly - SuperWebSocket.Service.exe.config for the app.config

 

<socketServer>
    <servers>
      <server name="SuperWebSocket"
              serviceName="SuperWebSocket"
              ip="Any" port="2011" mode="Async"
              commandAssembly="SuperWebSocket.Samples.JsonCommandAssembly">
      </server>
    </servers>
    <services>
      <service name="SuperWebSocket"
               type="SuperWebSocket.WebSocketServer, SuperWebSocket" />
    </services>
  </socketServer>

 

The Setup and Test code is a follows...

 

[TestFixtureSetUp]
        public virtual void Setup()
        {
            LogUtil.Setup(new ConsoleLogger());
            var serverConfig = ConfigurationManager.GetSection("socketServer") as SocketServiceConfig;
            if (!SocketServerManager.Initialize(serverConfig))
                Assert.Fail("SocketServerManager was not able to initialize");

            m_socketServer = SocketServerManager.GetServerByName("SuperWebSocket") as WebSocketServer;
            Assert.IsNotNull(m_socketServer, "Not able to create socketServer by name");
        }
[Test]
        public void Message_CHAT_SocketTest()
        {
            WebSocket webSocketClient = new WebSocket("ws://127.0.0.1:2011/websocket", "basic");
            webSocketClient.OnClose += new EventHandler(webSocketClient_OnClose);
            webSocketClient.OnOpen += new EventHandler(webSocketClient_OnOpen);
            webSocketClient.OnMessage += new EventHandler<MessageEventArgs>(webSocketClient_OnMessage);
            webSocketClient.Connect();

            if (!m_OpenEvent.WaitOne(1000))
                Assert.Fail("Failed to open session ontime");

            LogUtil.LogInfo("About to send CHAT message");

            webSocketClient.Send("CHAT {'Sender':'Kerry', 'Receiver': 'Linda', 'Content':'Where are you now?'}");

            webSocketClient.Close();

            if (!m_CloseEvent.WaitOne(1000))
                Assert.Fail("Failed to close session ontime");
        }

Setting a breakpoint and debug output in CHAT - but it's never hit?
public class CHAT : JsonSubCommand<ChatMessage>
    {
        protected override void ExecuteJsonCommand(WebSocketSession session, ChatMessage commandInfo)
        {
            System.Diagnostics.Debug.WriteLine("CHAT MESSAGE handler");
        }
    }

Thanks for any input.

Jim

 

 

 

 

Coordinator
May 18, 2011 at 2:28 AM
It seems you didn't start the server?
Missing a line below after initialize:
SocketServerManager.Start();
May 18, 2011 at 5:25 AM

Hi kerryjiang,

My apologies - I didn't show it in the snippets above - but I do have a Start() method call in the fixture [Setup], and it is being called.

The best I know at this point is the difference in using the config setup (does not work) vs. a programmatic setup (does work).
Using the app.config setup style does not pass along the subprotocol option values into the WebSocketServer Setup method

Is there another piece of configuration that is missing the config xml that should be set, or a different way to call SocketServerManager.Initialize() method?

Using the ProgrammaticSetup() method (shown in sample below) to initialize the WebSocketServer - everything works as expected, and I see the JsonCommandAssembly "CHAT" method being called.

The difference is that using the app.config / SocketServerManager.Initialize() method - WebSocketServer.Setup() (line 66) does not find any subProtocolValue in config.Options.GetValue("subProtocol") - as such, there is no subprotocol initialized.  I don't see anywhere where WebSockServer applies the "commandAssembly" when initialized from configuration file.


 

 

public override bool Setup(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory, ICustomProtocol<WebSocketCommandInfo> protocol)
        {
            string subProtocolValue = config.Options.GetValue("subProtocol"); // value is null when using config file
            if (!string.IsNullOrEmpty(subProtocolValue))
            {
                ISubProtocol<TWebSocketSession> subProtocol;
                if (AssemblyUtil.TryCreateInstance<ISubProtocol<TWebSocketSession>>(subProtocolValue, out subProtocol))
                    m_SubProtocol = subProtocol;
            }

            if (m_SubProtocol != null)
                m_SubProtocol.Initialize(config);

            if (!base.Setup(rootConfig, config, socketServerFactory, protocol))
                return false;

            if (string.IsNullOrEmpty(config.Security) || "none".Equals(config.Security, StringComparison.OrdinalIgnoreCase))
                m_WebSocketUriSufix = "ws";
            else
                m_WebSocketUriSufix = "wss";

            return true;
        }

Thanks again,

Jim

 

 

The entire test file is below if that helps:

 

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Threading;
using NUnit.Framework;
using SuperSocket.Common;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketEngine;
using SuperSocket.SocketEngine.Configuration;
using SuperWebSocket;
using SuperWebSocket.Client;
using SuperWebSocket.SubProtocol;

namespace ClientTestComandAssembly
{
    [TestFixture]
    public class TestCommandAssembly
    {
        protected WebSocketServer m_socketServer;
        private AutoResetEvent m_MessageReceiveEvent = new AutoResetEvent(false);
        private AutoResetEvent m_OpenEvent = new AutoResetEvent(false);
        private AutoResetEvent m_CloseEvent = new AutoResetEvent(false);
        private string m_CurrentMessage = string.Empty;

        private bool useProgrammaticSetup = false;

        [TestFixtureSetUp]
        public void setup()
        {
            if (useProgrammaticSetup)
                ProgrammaticSetup(); 
            else
                AppConfigSetup();  // this DOES NOT load the 'CHAT' command
        }
        public void ProgrammaticSetup()
        {
            LogUtil.Setup(new ConsoleLogger());

            Assembly jsonCmdAsm = Assembly.LoadFrom("SuperWebSocket.Samples.JsonCommandAssembly.dll");

            m_socketServer = new WebSocketServer(new BasicSubProtocol(new List<Assembly> { jsonCmdAsm }));
            m_socketServer.Setup(new RootConfig(), new ServerConfig
            {
                Port = 2011,
                Ip = "Any",
                MaxConnectionNumber = 100,
                Mode = SocketMode.Sync,
                Name = "SuperWebSocket Server"
            }, SocketServerFactory.Instance);
        }

        public virtual void AppConfigSetup()
        {
            LogUtil.Setup(new ConsoleLogger());
            var serverConfig = ConfigurationManager.GetSection("socketServer") as SocketServiceConfig;
            if (!SocketServerManager.Initialize(serverConfig))
                Assert.Fail("SocketServerManager was not able to initialize");

            m_socketServer = SocketServerManager.GetServerByName("SuperWebSocket") as WebSocketServer;
            Assert.IsNotNull(m_socketServer, "Not able to create socketServer by name");
        }

        [SetUp]
        public void StartServer()
        {
            bool started = m_socketServer.Start();
            Assert.IsTrue(started, "SocketServer was not able to start");
        }

        [Test]
        public void ConnectionTest()
        {
            WebSocket webSocketClient = new WebSocket("ws://127.0.0.1:2011/sample", "basic");
            webSocketClient.OnClose += new EventHandler(webSocketClient_OnClose);
            webSocketClient.OnOpen += new EventHandler(webSocketClient_OnOpen);
            webSocketClient.OnMessage += new EventHandler<MessageEventArgs>(webSocketClient_OnMessage);
            webSocketClient.Connect();

            if (!m_OpenEvent.WaitOne(1000))
                Assert.Fail("Failed to open session ontime");

            webSocketClient.Close();

            if (!m_CloseEvent.WaitOne(1000))
                Assert.Fail("Failed to close session ontime");
        }

        [Test]
        public void Message_CHAT_SocketTest()
        {
            WebSocket webSocketClient = new WebSocket("ws://127.0.0.1:2011/sample", "basic");
            webSocketClient.OnClose += new EventHandler(webSocketClient_OnClose);
            webSocketClient.OnOpen += new EventHandler(webSocketClient_OnOpen);
            webSocketClient.OnMessage += new EventHandler<MessageEventArgs>(webSocketClient_OnMessage);
            webSocketClient.Connect();

            if (!m_OpenEvent.WaitOne(1000))
                Assert.Fail("Failed to open session ontime");

            LogUtil.LogInfo("About to send CHAT message");

            webSocketClient.Send("CHAT {'Sender':'kerry', 'Receiver': 'Linda', 'Content':'Where are you now?'}");
            
            Thread.Sleep(500); // give the server time to respond/handle message
            webSocketClient.Close();

            if (!m_CloseEvent.WaitOne(5000))
                Assert.Fail("Failed to close session ontime");
        }

        [TearDown]
        public void StopServer()
        {
            m_socketServer.Stop();
        }

        // helper event routines
        void webSocketClient_OnOpen(object sender, EventArgs e)
        {
            m_OpenEvent.Set();
        }
        void webSocketClient_OnMessage(object sender, MessageEventArgs e)
        {
            m_CurrentMessage = e.Message;
            m_MessageReceiveEvent.Set();
        }
        void webSocketClient_OnClose(object sender, EventArgs e)
        {
            m_CloseEvent.Set();
        }
    }
}

 

 

Coordinator
May 18, 2011 at 5:45 AM
You may found the point.
You need to define subProtocol in your server instance's configuration node like the code below:
<server name="SuperWebSocket"
serviceName="SuperWebSocket"
subProtocol="SuperWebSocket.SubProtocol.BasicSubProtocol, SuperWebSocket"
commandAssembly="......"
ip="Any" port="2011" mode="Async">
</server>
The programming experience is not good here. I'll improve the code to simplify the configuration!
Coordinator
May 18, 2011 at 6:08 AM

I have improved the code.

The subProtocol configuration is not required now!

Please get the latest from SVN!

May 18, 2011 at 6:50 AM

Thanks again - that was the major issue.

However, in the test case I still ran into the following exception:

TestFixture failed: System.NullReferenceException : Object reference not set to an instance of an object.
   at SuperSocket.Common.AssemblyUtil.GetImplementedObjectsByInterface[TBaseInterface](Assembly assembly) in e:\Build\SuperSocket\mainline\Common\AssemblyUtil.cs:line 59

When the BasicSubProtocol class is initialized - it is passed a List<Assembly> with a null item.  Assessmbly.GetEntryAssebly() is returning a null (most likely due to NUnit ?)
So I added the following to the base ctor to protect against  null's being added to the m_CommandAssemblies listing. 

public BasicSubProtocol(IEnumerable<Assembly> commandAssemblies)
        {
            if (commandAssemblies != null)
            {
                IEnumerable<Assembly> asmList = commandAssemblies.Where(a => a != null);
                m_CommandAssemblies.AddRange(asmList);
            }
            // m_CommandAssemblies.AddRange(commandAssemblies);
            SubCommandParser = new BasicSubCommandParser();
        }

        public BasicSubProtocol()
            : this(new List<Assembly> { Assembly.GetEntryAssembly() })
        {

        }

This prevents a call later on in GetSubCommands() from throwing the origin of the exception:

public IEnumerable<ISubCommand<TWebSocketSession>> GetSubCommands()
        {
            var subCommands = new List<ISubCommand<TWebSocketSession>>();

            foreach (var assembly in m_CommandAssemblies)
            {
                    subCommands.AddRange(assembly.GetImplementedObjectsByInterface<ISubCommand<TWebSocketSession>>());  // would throw exp if list had a null entry
            }

            return subCommands;
        }
Jim
May 18, 2011 at 6:57 AM

Excellent - you posted the code fixes before I could even finish my previous note!

Thanks again!

Jim

Coordinator
May 18, 2011 at 6:57 AM
SuperWebSocket\SubProtocol\BasicSubProtocol.cs
Did you update the above code?
I produced this issue in my local, and have fixed this issue.
Please take a look at this change:
m_CommandAssemblies.AddRange(commandAssemblies);
=>
m_CommandAssemblies.AddRange(commandAssemblies.Where(a·=>·a·!=·null));
May 18, 2011 at 7:00 AM

yes - all is good.  We just cross-posted.