5.4 Server development


  • 5.4.5 Simple verification of the server

    No mention to the implementation of the client, but visually experience the effect of the server development by using the HTML5 via chrome browser.

    • Simply prepare the server components

      Import the auany project in the eclipse

      Launch the limax.auany.Main.

      Note:Manually create the zdb directory in the current directory of the auany before the first launching the anany.

      Import the switcher project in the eclipse

      Launch the limax.switcher.Main


    • The parameters of the xmlgen

      Add -script parameter to generate the source code supported by the server script.

      Add -jsTemplate parameter to generate the source code of the javascript template.

      For Example:


      java –jar <path to limax.jar> xmlgen –script –jsTemplate example.server.xml                    
      

      After executing the command, there is a file named as template.js in the current directory.


    • Finish the experimental html

      Copy the limax.js to the current directory

      example.html


      <!DOCTYPE html>  
      <html> 
      <script type="text/javascript" src="limax.js"></script>  
      <body>  
      <script>  
          Copy and paste all the contents of the template.js 
      </script> 
      </body> 
      </html>            
      

      Modify the contents here:


      var login = {
          scheme : 'ws',
          host : '127.0.0.1:10001',   // configure the server address
          username : 'XXX',           // the username
          token : '123456',           // MUST use '123456' as the token
          platflag : 'test',          // use the test authentication module of the auany
          pvids : [100],              // PVID=100
      }            
      

      This configuration is related to the test module of the auany. Please refer the service-anany.xml and test module for the detailed information.


    • Add the Session manager class

      SessionManager.java


      import java.io.IOException;
      
      import limax.net.Config;
      import limax.net.Manager;
      import limax.net.ServerManager;
      import limax.net.Transport;
      import limax.provider.ProviderListener;
      import limax.provider.ProviderTransport;
      import limax.util.Trace;
      
      public class SessionManager implements ProviderListener {
          private ServerManager manager;
          @Override
          public void onManagerInitialized(Manager manager, Config config) {
              try {
                  this.manager = (ServerManager)manager;
                  this.manager.openListen();
              } catch (IOException e) {
                  if (Trace.isErrorEnabled())
                      Trace.error("SessionManager.onManagerInitialized", e);
                  this.manager.close();
              }
          }
          @Override
          public void onManagerUninitialized(Manager manager) {
          }
          @Override
          public void onTransportAdded(Transport transport) throws Exception {
              long sessionid = ((ProviderTransport) transport).getSessionID();
              if (Trace.isInfoEnabled())
                  Trace.info("SessionManager.onTransportAdded " + transport
                          + " sessionid = " + sessionid);
          }
          @Override
          public void onTransportRemoved(Transport transport) throws Exception {
              long sessionid = ((ProviderTransport) transport).getSessionID();
              if (Trace.isInfoEnabled())
                  Trace.info("SessionManager.onTransportRemoved " + transport
                          + " sessionid = " + sessionid);
          }
          @Override
          public void onTransportDuplicate(Transport transport) throws Exception {
              manager.close(transport);
          }
      }
                
      

      The onManagerInitialized is triggered when the server initiates, first the application resource required by the server running is loaded, then the openListen() is called after the load is finished. The openListen() first initializes all the global View firstly, then informs that the Switcher is ready and allows the client to connect.

      The nManagerUninitialized() is triggered when the server stops to clean all the application resource.

      The onTransportAdded message is triggered when the client connects the server to provide the opportunity to prepare the resource outside of the View system of the corresponding user. Then the user's SessionView is created.

      The onTransportRemoved message is triggered when the client disconnects from the server to provide the opportunity to release the resource except the View of the corresponding user. In most case, this message is recorded as log.

      The onTransportDuplicate message is triggered when the client repeatedly login. If closing transport here, the session of the previous user will be kicked; if nothing done here, the later user will be forbidden to login.


    • Modify service-ServerExample.xml

      Add the attribute className = "SessionManager" in the Provider element so that the SessionManager notification message could be created when server launches.

      Modify the attribute level = "INFO" in the Trace element and set the log level to get more information, such as the log in the above SessionManager.java which is recorded in the INFO level.

      Under the condition that not use the GlobalId service components, the GlobalId element could be commented, and at the same time the useGlobalId="true" attribute in the example.server.xml should be deleted and regenerated, or the new GlobalId element will be generated in the next generating code process.


    • The server launches the main function

      Main.java


      import limax.xmlconfig.Service;
      
      public class Main {
          public static void main(String[] args) throws Exception {
              Service.run("service-ExampleServer.xml");
          }
      }         
      

      Add the main function of the server in the src directory, and create the zdb directory in the current directory.

      Run the Main and get the return information in the eclipse console.

      2015-03-19 21:10:08.322 INFO <main> ServiceConf load service-ExampleServer.xml

      2015-03-19 21:10:08.323 INFO <main> ServiceConf runTaskBeforeEngineStart

      2015-03-19 21:10:08.354 FATAL <main> zdb start begin

      2015-03-19 21:10:08.410 FATAL <main> zdb start end

      2015-03-19 21:10:08.410 INFO <main> ServiceConf startNetEngine

      2015-03-19 21:10:08.433 INFO <main> ServiceConf runTaskAfterEngineStart

      2015-03-19 21:10:08.465 INFO <main> ProviderManager SessionManager@15327b79 opened!

      2015-03-19 21:10:08.471 INFO <limax.net.io.NetModel.processPool.17> provider client manager limax.net.StateTransportImpl (/127.0.0.1:11705-/127.0.0.1:10100) setInputSecurityCodec key = compress = false

      2015-03-19 21:10:08.472 INFO <limax.net.io.NetModel.processPool.17> provider client manager limax.net.StateTransportImpl (/127.0.0.1:11705-/127.0.0.1:10100) setOutputSecurityCodec key = compress = false

      2015-03-19 21:10:08.475 INFO <ProviderConnectorExecutor.ExampleServer.18> SessionManager@15327b79 onTransportAdded limax.net.StateTransportImpl (/127.0.0.1:11705-/127.0.0.1:10100)

      2015-03-19 21:10:08.480 INFO <limax.net.Engine.protocolScheduler.21> provider had bind success! pvid = 100

      In the last line, pvid = 100, it is the PVID assigned in the exmaple.share.xml file. Everything is OK and the experiment could begin.


    • Experiment 1

      No any source code written, launch the chrome browser, click the F12 to active the debug window, drag the prepared example.html in the chrome.

      check the log of the server:

      2015-03-20 16:37:56.679 INFO <limax.net.Engine.applicationExecutor.24> SessionManager.onTransportAdded limax.provider.ProviderTransportImpl (49152 - /127.0.0.1:40505) sessionid = 49152

      This line is recorded by the previous code of the ViewManager, where the client connects and the user's sessionid is 49152.

      check the chrome console:

      About one minute later, the keepalive is displayed on the chrome console. That indicates that the limax.js periodically sends the keepalive message to the server.

      Stop the server and check the chrome console:

      The ctx.oneerror in the copied template source code reports the exception, then the connection is closed and the ctx.onclose reports the reason --- error code 17. In the codition that the error code existes, it is no need to care the above exception, but directly find the description file defines.beans.xml in the Limax framework. Then check this line:


      <enum name="SWITCHER_PROVIDER_UNBIND" value="17" />         
      

      The Switcher reports that the Provider unbounds, which means the server stops.


    • Experiment 2

      Launch the server and refresh the chrome web page.

      Then return to the previous initial status.

      Launch a new instance of the chrome , and drag the example.html in the chrome.

      Check the chrome console:

      There is error code 3008, which corresponds to the below:


      <enum name="PROVIDER_KICK_SESSION" value="3008" />         
      

      This means that SessionManager.onTransportDuplicate works correctly and the privous user's session is kick off.


    • Experiment 3

      Generally speaking, an application at least has a SessionView. The user's application-level information should be persistent.


      private MySessionView(SessionView.CreateParameter param) {
          super(param);
          // bind here
          long sessionid = param.getSessionId();
          bindMytable(sessionid);
          Procedure.execute(() -> {
              xbean.MyXbean xb = table.Mytable.insert(sessionid);
              if (xb != null) xb.setVar0(100);
              return true;
          });
      }        
      

      The above souce code associates the bind0 field of the view to the current sessionid, then uses a storage procedure to judge whether the record of the sessionid in the Mytable is existed. If the record is not existed, a new record will be created and the var0 field in the record will be initiated as 100. Binding first, then using the storage procedure to decide whether to initiate the recorde, is the common way.

      Run the server and refresh the chrome web page:

      From the above information, the bind0 field of the user sessionid = 49152 has been created in the client and the var0 = 100 is set in the bind0 field.

      Then refreshing the chrome web page, there is no change for the content in the web page.

      Modify the login.username in the example.html, change to a never used usernanme.

      Refresh the chrome web page:

      The sessionid = 53248 here is noted, which means that the sessionid distincts the user.


    • Experiment 4


      private MySessionView(SessionView.CreateParameter param) {
          super(param);
          // bind here
          long sessionid = param.getSessionId();
          bindMytable(sessionid);
          Procedure.execute(() -> {
              xbean.MyXbean xb = table.Mytable.insert(sessionid);
              if (xb != null) xb.setVar0(100);
              setVar0("oh, my god. it works.");
              setVar0("the sequence is right.");
              return true;
          });
          setVar0("hello world");
      }     
      

      The setVar0 in three View are supplemented in the above code. Run the server and refresh the chrome web page.

      Line 1: the var0 of the view is created as "hello world". Even though the set funtion is call in the last line of the code, the previous storage procedure is executed in the other thread. So it is not surprise that this function is executed firstly.

      Line 2: the var0 of the view is replaced as the "oh, my god.it works.".

      Line 3: the var0 of the view is replaced as the "the sequence is right.".

      Line 4: the bind0 field of the view is initiated just like the previous experiment.

      The bind0 here is actually initiated by the bindMytable and set in another storage procedure. So we do not know when it happens.

      So, modify the example.html and change to another never used username.

      In the above information, it is certain that the insert operation occurs before the two setVar0, but why is the set of the bind0 in the last?

      Further understanding the previous description in this manual is needed:

      "It is specially noted that there is no any assumption for the change sequence of bind field of the same View, and these change have the transaction atomicity feature."

      And the set for the var0 in the view could explain:

      "The client could reproduce the fields' order in the same View which is modified by the server"

      The variable's set order could be guaranteed, but the bind could not. The reason is simple. In the storage procedure, the repeated modification in the same xbean is possible, and only the last modification result will be committed from the consideration of the transaction.

      Replace setVar0("the sequence is right.") with _setVar0("the sequence is right.").

      In the above information, the bind0 is placed in the Line 2. As already made clear, it is no need to care where it appears.

      In the Line 3 and Line 4, "the sequence is right." appears to the front. The signifiance of the _set version is reflected here: when using the _set version, the data of the field is immediately set to the view and it is no need to wait for the success of the transaction.

      Add two lines setVar0("hello world") and setVar0(null) after the setVar0("hello world").

      Line 2: the var0 of the view is created as "hello world".

      Line 3: the var0 of the view is "hello world", and the status is TOUCH.

      Line 4: the var0 of the veiw is "hello world", which is deleted and the status is DELETE.

      Line 5: the var0 of the view is created as "the sequence is right.".

      Actually, the TOUCH achieves the optimization to the network traffic. If the server finds that the data of the field has no change, it simply informs the client that the field is TOUCHed.

      If the field of the view need to be deleted, it is set as null. In addition, when the record of the bind is deletd from the table, the bind field is set as null.


Prev Next