Tuesday, September 11, 2012

How to Upgrade to Web Client Library 6 and Later


Lightstreamer JavaScript client version 6 brings some new interesting features and some long-awaited changes. This time we’ve chosen not to pass from the deprecate-first remove-then path as we’ve always done: in fact the entire API was completely designed from scratch to be more flexible and easy to use. Unfortunately, this also means that some effort is necessary to migrate your code based on Lightstreamer HTML Client 4 or 5 to the new Lightstreamer JavaScript Client 6 and later.
This guide was written to help you with the migration. Only the simplest way to do the porting is illustrated; it is obviously possible to use more complicated techniques. Also following only the guide you will not exploit many of the new capabilities of the library, so, before doing the porting I suggest you to check the changelog, the new Development Guide and the Tutorial.



While doing the porting you should keep the new API documentation at hand. To help with the comparison you may want to keep an eye on the old documentation too.

AMD vs. Globals

In the past, our HTML client library contained some html files that had to be deployed with the js ones. Due to this you were limited on deploying the lightstreamer client libraries on the same server where the html front-end was hosted.
All of those files were eliminated: you can now deploy the Lightstreamer JS client library file anywhere on the web, even on a third party CDN, and just import it in your html file(s).

The library is now fully modularized AMD style even if, at least at the moment, we do not distribute the single modules each one in its own file. Instead you’ll find three version of an all-in-one file.

  • lightstreamer.js contains all the modules define calls. You need an AMD loader (like requirejs) to use this file: include our library after the AMD loader and then start coding using the require method to load our classes:
    require([
    "LightstreamerClient"],function(LightstreamerClient) {
     var testClient = new LightstreamerClient(...);  
     [...]
    });
  • lightstreamer_globals.js if your project does not already use an AMD loader and you don’t want to add one you can include this file that will automatically create a global object per each of our public classes, so after including the js library, you directly instantiate our classes:var testClient = new LightstreamerClient();  
    [...]
  • lightstreamer_namespace.js if your project comprehends AMD modules other than Lightstreamer’s ones you probably want to avoid name collisions. If that’s the case you can use this version of the library like this: require(["Lightstreamer/LightstreamerClient"],function(LightstreamerClient) {
     
    var testClient = new LightstreamerClient(...);  
     [...]
    });

  • If you want global names but would like to have one single entry point (like using the old Lightstreamer.avoidLSGlobals flag set to true) you don’t have a ready-made solution. Don’t panic, our generator tool (available on the Download page; look for Customize Lib) will help you out making it possible to generate a namespaced library to be used like this:var testClient = new Lightstreamer.LightstreamerClient(...);  
    [...]

QUICK ACTION: remove lscommons.js and lspushpage.js inclusions from your pages and include the lightstreamer_globals.js file. If you were using the Lightstreamer.avoidLSGlobals flag, head for the generator and create a namespaced library to be used in place of lightstreamer_globals.js.


NOTE: you will probably not use all of the available classes of the Lightstreamer JS client: the
generator tool (available on the Download page; look for Customize Lib) makes it possible to create a library file containing only the required classes.

Client-Side Errors

I know some of you awaited this change for years :) We’ve finally simplified the handling of client side errors.

We had different (admittedly too many) ways to consume those errors:

  • PushPage.onClientError and PushPage.onClientAlert callbacks.
  • LightstreamerEngine.onClientError and LightstreamerEngine.onClientAlert callbacks.
  • Context.setDebugAlertsOnClientError to enable/disable the use of alerts to show such errors (a Context instance was available on both PushPage and LightstreamerEngine instances)
  • Context.setRemoteAlertsOnClientError to enable/disable the forwarding of the errors to the server (a Context instance was available on both PushPage and LightstreamerEngine instances)

Now all of these methods/callbacks are gone and there is only one flexible way to consume error messages: with this version of the library we’re exposing our internal logging facility (that anyway was partially rewritten too) so that you will be able to use it to consume the errors.

To mimic the old behavior we need to hook an appender to the logging system and start listening to ERROR events on every category. Obviously some messages changed, others were removed and others are completely new.
The following code shows how to mimic the old code via your own callback, via remote messages to the server or via window.alert:

 //First we need to init the logging system as it is disabled
 //by default.
 var loggerProvider = new SimpleLoggerProvider();
 LightstreamerClient.setLoggerProvider(loggerProvider);

 //CONSUME AS CALLBACK

 //Create a FunctionAppender: we want it to be filtered
 //by level (ERROR) and we want to consume all of the available
 //categories (*)
 var fc = new FunctionAppender("ERROR","*",function(lineOfLog) {
   //consume the lineOfLog string as you prefer;
   //the string itself contains time category and message
 });

 //add such appender to the logging system
 loggerProvider.addLoggerAppender(fc);

 //CONSUME AS ALERT

 //we simply need to create and add the proper appender
 //the 1 in the constructor of AlertAppender is the number of
 //messages to gather before shwoing the alert
 loggerProvider.addLoggerAppender(new AlertAppender("ERROR","*",1));

 //CONSUME ON THE SERVER

 //This appender requires a configured LightstreamerClient to send
 //messages to the server; it is suggested to use the same instance    
 //you use in your application
 var lsClient = new LightstreamerClient(...);  
 ...
 //Create and add the RemoteAppender
 loggerProvider.addLoggerAppender(new RemoteAppender("ERROR","*",lsClient));

Note that only basic functionalities were shown in the above examples, check the API documentation for the details.

QUICK ACTION: remove all onClientError and onClientAlert callbacks implementations, substitute them with FunctionConsumers as explained in the CONSUME AS CALLBACK section of the above example.

Remove setDebugAlertsOnClientError(true) calls and substitute them with AlertConsumers as explained in the CONSUME AS ALERT section of the above example.

Remove setRemoteAlertsOnClientError(true) calls and substitute them with RemoteConsumers as explained in the CONSUME ON THE SERVER section of the above example.

PushPage/LightstreamerEngine

The PushPage class does not exist anymore. Also the LightstreamerEngine was removed from the APIs. All of their functionalities are now collapsed in the LightstreamerClient class.

Also the connection and policy properties of LightstreamerEngine (respectively Connection and Policy instances) were removed. On the other hand the new connectionOptions, connectionDetails and connectionSharing (ConnectionOptions, ConnectionDetails and ConnectionSharing instances) have been introduced as part of LightstreamerClient.

Some search and match is required to port the old behavior to the new APIs. The below box gives some useful hints to do it effectively.

QUICK ACTION: Replace your PushPage instance with a LightstreamerClient instance. Get rid of onEngineCreation/onEngineReady/onEngineLost callbacks.

Port calls made to PushPage, LightstreamerEngine, connection and policy to your LightstreamerClient, connectionSharing, connectionOptions and connectionDetails.

Any call made during the execution of the onEngineCreation/onEngineReady callbacks can now be made immediately after LightstreamerClient instantiation.

No need to dispose the LightstreamerEngine pointer during onEngineLost callbacks; whatever you were doing there can probably be moved to the DISCONNECTED handler.

Any other callback should be replaced by the proper listener. More on this on the Callbacks section of this guide.

As said new calls may have different names, matching most of the old calls with new calls is trivial. Here a list of the most complex cases:
  • changeStatus is now replaced by connect and disconnect calls. If for any reason (that I will never understand) you were explicitly asking for a POLLING session in your changeStatus call you can still do that using the new ConnectionOptions.setForcedTransport method.
  • createEngine/seekEngine functionalities were replaced by a single ConnectionSharing.enableSharing method.
    NOTE: If you were using the
    NEW_SESSION directive in your createEngine call simply remove the call, this is the default behavior if no sharing is configured.
    Otherwise use the name you were using in the
    createEngine/seekEngine call as first parameter and then, if you were using createEngine with SHARE_SESSION  set ATTACH and CREATE as 2nd and 3rd parameters; use ABORT and CREATE if you were using FAIL;  use ATTACH and WAIT if you were using a seekEngine call.
    NOTE: to avoid wasting you should call enableSharing before calling connect. Not a requirement though.
  • addTable/removeTable are now called subscribe and unsubscribe. More on this on the Table section of this guide
  • setLSHost and setLSPort have been collapsed in the ConnectionDetails.setServerAddress method. It is also necessary to specify a protocol (http or https) now. See Lightstreamer JS Client - Deployment Config Matrix and the API documentation for details.
  • The PushPage.bind and the Context.setDomain methods are no more; see dedicated section.

PushPage.bind - Context.setDomain

Another big news is the removal of the bind method from the PushPage object and all the requirements it had. You can create now your LightstreamerClient, call enableSharing, connect and subscribe at any time.
Also with the removal of the Context class there is no way to set the domain on the page through our APIs: the Lightstreamer library now adapts itself to the domain it finds at any given moment on the page. All the possibilities of what will happen on the basis of the domain set, the browser, the front-end host and the Lightstreamer hosts is detailed in the  Lightstreamer JS Client - Deployment Config Matrix

QUICK ACTION: if your application wasn’t using setDomain to change the domain on the page just remove the PushPage bind calls; otherwise remove both Context setDomain calls and PushPage bind calls and add a document.domain = "yourdomain.com" statement to your page (preferably before the Lightstreamer-related code)

Table Classes

The NonVisualTable was renamed to Subscription and is now the only “Table” class.
Once configured it can be activated through the LightstreamerClient.subscribe call (and deactivated through LightstreamerClient.unsubscribe).
The old VisualTable classes now acts as listeners for this new Subscription class (and thus it is possible to feed multiple VisualTable with a single subscription).

QUICK ACTION: per each Table instance in your application create a Subscription instance. Port all the subscription-related calls to such instance. Note that some function names are slightly different, and also their parameters need to be specified differently; matching is quite easy anyway.
Visual-related calls are explained in the next box. Callbacks porting is explained in the Callbacks section.

The following calls need a special treatment:
  • The order of the parameters in the constructor is now different. Also it is not possible to specify null to read the group/schema from the html page, to keep such behavior it is necessary to use the extractFieldList and extractItemList methods of DynaGrid/StaticGrid.
  • setCommandLogic is now splitted across different setCommand* and setCommandSecondLevel* calls. The flag parameter is then inferred by the values used for such calls.
  • The LightstreamerClient.subscribe method, unlike the addTable one, only accepts one parameter (the Subscription). To call the unsubscribe method the same object is required, not its id as such id does not exist anymore.

VisualTable

OverwriteTable, ScrollTable, MetapushTable are now collapsed into the StaticGrid class. The ScreenTableHelper functionalities have been collapsed there too.
DynaScrollTable, DynaMetapushTable, MultiDynaMetapushTable are now collapsed in the DynaGrid class. Note that such DynaGrid class can also be used as a “DynaOverwriteTable”

The new classes are not stricly Subscription-bound as the old Table were. This gives much more fliexibility to the visual framework of Lightstreamer (i.e.: you may even use the new grids without bounding’em to any subscription).

QUICK ACTION: per each VisualTable instance create the proper *Grid instance and port the visual-related call to it (my personal suggestion is to use DynaGrid even if you were using OverwriteTable, ScrollTable or MetapushTable). Again some function names are slightly different, check the API for details.

If you were using a ScreenTableHelper you can port its logic directly into your StaticGrid (thus no addScreenTableHelper calls or id matching needed anymore).

Bind each *Grid to the related Subscription using the Subscription.addListener call.

Callbacks porting is explained in the Callbacks section.

The following calls need a special treatment:
  • setClearOnAdd setClearOnRemove setClearOnDisconnect do not exist anymore,  setAutoCleanBehavior substitutes them with a slightly different logic. The clean method can be used to clean the grid at any moment.
  • The new classes are not “added” anymore but the library still needs to know when you’re ready to bound the html to your grids: you can either specify a flag in the constructor to make the bound at construction time, or you can bind’em later using the parseHtml call.
  • The id specified in the addTable calls was also used to bind a Table to the related html snippet. The same binding is now obtained specifiyng such id in the constructor of the Grid.
  • The logic behind showValues is now changed and moved into the new updateRow function.

The html templates need some adjustments too. Search for our custom html properties and rename them as follows:
  • table becomes data-grid
  • item becomes data-item
  • row becomes data-row
  • field becomes data-field
  • source becomes data-source
note that their values remain the same.

If you were using elements different from <div> <span> and <input> in the template of a Dyna*Table you have to add a setNodeTypes call to the new DynaGrid instance passing in an array of strings containing the name of the elements used or the grid will not recognize the cells (e.g.: myGrid.setNodeTypes(["td","p"])).



VisualUpdateInfo 

Most calls from the old VisualUpdateInfo class can be easily ported to the new VisualUpdate one. There is one call tough that can't be directly matched: getServerValue. In this case the new getValue from the Grid class can be leveraged this way:

Old Code 
table.onChangingValues = function(itemPos, visualUpdateInfo, itemName) { 
  //stuff 
  var val = visualUpdateInfo.getServerValue("field"); 
  //more stuff 
};

New Code 
grid.addListener({ 
  onVisualUpdate: function(key,visualUpdate,dom) { 
    //stuff 
    var val = visualUpdate.getChangedFieldValue("field") || grid.getValue(key,"field");
    //more stuff 
  } 
});


Callbacks

We’ve moved from a “define a function on one of our objects to be internally called as a callback” model to an “add a listener that receives events” model.
The big plus of this change is the possibility to have different handlers for the same event.

Take a look to the following example to quickly understand how to move from one model to the other:

Old Code
pushPage.onStatusChange = function(newStatus) {
 //do stuff with newStatus
};

New Code
lsClient.addListener({
 onStatusChange: function(newStatus) {
   //do stuff with newStatus
 }
});

Per each addListener call in the APIs a related *Listener interface is defined. Note that you only need to define the events you want to be notified on, no need to implement the whole interface.

QUICK ACTION: search all the callbacks you defined on our objects and substitute with addListener calls as shown in the above example. Note that some callbacks may have different names; as an example onStart is now called onSubscription, other callbacks have been moved, as an example the onServerDeny callback from PushPage is now the onSubscriptionError of the SubscriptionListener.

NOTE: if you were using the this keyword in your callbacks implementation you’ll have to change your code: previously such this would refer to the attached instance while now refers to the *Listener instance. Be careful.

Subjects Not Covered

Even after following this guide you may still have doubts about how to correctly port something. That’s fine, I appreciate I would need to write a complete book to cover all the differences between the old and the new APIs, so drop us a line here or on our forum and we’ll help you to sort out any remaining porting issues.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.