Tuesday, February 17, 2015

Writing Remote PHP Adapters for Lightstreamer

As most (if not all) of you know, PHP is a widely adopted server-side scripting language, mainly focused on web development through the availability of web server native's module.

However, PHP could also be used for command-line scripting through the PHP Commnad Line Interface. This way you can leverage its power and simplicity to create general-purpose applications running in a shell.

In this post we will use such feature to illustrate the development of custom Remote Adapter Sets for Lightstreamer, written in PHP.

Working With Remote Adapters

Lightstreamer provides an easy and efficient  way to deploy Adapter Sets outside of the Java Virtual Machine process running the Server, through the Adapter Remoting Infrastructure (ARI). Such Adapters are therefore called Remote, because the interaction between them and the Server requires information to be exchanged over TCP sockets.

Remote Adapters offer physical decoupling between the code of the Server and the code of the Adapters for a better usage of the resources, and the possibility to implement the Adapters in any language that provides a basic support for plain TCP sockets. SDKs for .NET AdaptersNode.js Adapters and Java Remote Adapters are already provided as part of the Lightstreamer distribution.

The following picture shows the general architecture of the ARI:

The Proxy Data Adapter and Proxy Metadata Adapter are ready-made Adapters, written in Java and plugged into the Lightstreamer Kernel, which expose respectively the Data Provider and the Metadata Adapter interface over TCP/IP sockets and a specific network protocol. 

In the reminder of this post, we are going to develop two examples of Remote Adapters which will implement the ARI Protocol, based on a set of PHP scripts to be executed from the command line.

The PHP Way

The exercises we want to show you are the PHP ports of the Java Adapters already published on our GitHub repository. Take a look at the followings links to get a general understanding of such examples:
The PHP Adapter implementations are structured as follows:
  1. A shell script entry point.
  2. The autoload.php file, which loads the required classes.
  3. The lightstreamer hierarchy directory structure, containing all the PHP classes (a file for each class), which implement the ARI Protocol.
Such hierarchy is the core of our examples and acts as an API which provides an higher-level of abstraction over the internal implementation of the network protocol. This means that writing an Adapter Set in PHP does not require to deal with direct socket programming: just implement the interfaces provided and the library will take care of managing all the low-level TCP interactions.

As you can see from the picture below, the API is organized in much the same way as the the Remote Java Adapters and .NET Adapters, in terms of classes, interface and exceptions. So, if you are already familiar with such official APIs, it should be quite simple for you to use the PHP version too.

The examples shown in this post require that the pthreads module is installed into your PHP environment. You can get detailed information on how to properly install the module here.

The Hello World Adapter Example

The well-known Lightstreamer Hello World example simply shows on a web page two alternated strings, "Hello" and "World", followed by the current timestamp: all these values are pushed by Lightstreamer server to the browsers.

The helloworld.php file is entry point for this PHP example. In this script we define a few classes and a bit of initialization code to start communicating with Lightstreamer Server.

The GreetingsThread class is a very simple Thread whose purpose is to generate the "greetings" events to be sent to the Proxy Adapter. It also comes with a coupe of methods (pause() and resume()) to manage its execution, according to the life-cycle of the subscribed items.

The HelloWorldDataAdapter class implements the IDataProvider interface, which is the PHP equivalent of the Java DataProvider interface.

The StartServer class is a simple utility used to configure and start a Server instance, an abstract class extended by DataProviderServer, which is the corresponding implementation you can find in the Remote Java Adapters.

The initialization code setups and activates the communication with the Proxy Adapters:

After creating and starting the GreetingThread, we pass its handle to an instance of the HelloWorldDataAdaper. Then, we create a DataProviderServer, initialized with the reference to HelloWorldDataAdapter. Since the Proxy Data Adapter to which our remote PHP Adapter will connect needs two connections, we create and setup the StarterServer with two different TCP ports (6661 and 6662 as configured in the beginning ) in order to make it create two stream sockets. Finally, we start DataProviderServer.

The full code example is available on GitHub, along with the complete instructions to deploy and run the Adapter against you local installation of Lightstreamer:

The Chat Adapter Example

Now, let's see another example of using the same API to re-implement the Basic Chat Demo- Java Adapter. The exercise is very useful because it also introduces the MetadataAdapter implementation,  which is required to deal with sessions, users and notifications.

In the chat.php script, we define the ChatMedataAdapter class, which extends the LiteralBasedProvider class (the PHP equivalent of the Java LiteralBasedProvider class) and the ChatDataAdapter class, which implements the IDataProvider interface, exactly as we did earlier with the HelloWorldDataAdapter. These classes, together, realize the Chat Adapter Set.

The following is the sequence of steps involved in the collaboration between the two classes to implement the logic of the Chat Demo application:
  1. One or more clients connect to the Chat Adapter Set.
  2. The ChatMetadataAdapter's notifyNewSession method is invoked and the session information is stored and indexed by the session_id:
  3. The first client subscribes to the "chat_room" item, enabling the ChatAdapter to generate real-time updates:
  4. A client sends a chat message.
  5. The ChatMetadataApdapter's notifyNewMessage is invoked: the session retrieved through the session_id allows to identify the message originator, by extracting the stored info about its IP and User Agent. Then, Message, IP and User Agent are forwarded to the ChatDataAdapter.
  6. The ChatDataAdapter wrap such information into the real-time updates to be finally pushed to the ProxyAdapter, by invoking the ItemEventListener's update method.
  7. Finally, the message is displayed on the web page of all connected clients.
  8. When a client disconnects, the ChatMetadataApdapter's notifySessionClose is invoked and relative session information is discarded.
  9. When no more clients are connected, the ChatDataAdapter's unsubscribe method is invoked.

In the initialization code we have an important difference with respect to the HelloWorld Adapter example: the introduction of the MetadataProviderServer class, which extends the Server class and provides all the logic needed to connect to the Proxy Metadata Adapter running on Lightstreamer Server.
So, as shown in the snippet above, this time we need to initialize two StarterSever instances:
  • one for the MetadataProviderServer, which will be connected to the metadata request/reply TCP port
  • and one for the DataProviderServer, which will be connected both to the data request/reply and data notify TCP Port, as we did for the DataProviderServer in the previous example.
Finally, we start both the MetadataProviderServer and the DataProviderServer.

Also in this case, the code example and the instructions to deploy and start the service, are available on GitHub:


Lightstreamer enables the development of Remote Adapters based on any language that allows client TCP socket programming. In this post we have focused on the PHP environment, showing how this powerful platform could be successfully attached to Lightstreamer, as already done with other technologies.

No comments:

Post a Comment

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