Skip to content

Lightstreamer/Lightstreamer-example-HelloWorld-adapter-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lightstreamer - "Hello World" Tutorial - Python Adapter

The "Hello World" Tutorial is a very basic example, based on Lightstreamer, where we push the alternated strings "Hello" and "World", followed by the current timestamp, from the server to the browser.

This project, of the "Hello World with Lightstreamer" series, will focus on a Python port of the Java Adapter illustrated in Lightstreamer - "Hello World" Tutorial - Java Adapter.

As an example of Clients Using This Adapter, you may refer to the Lightstreamer - "Hello World" Tutorial - HTML Client and view the corresponding Live Demo.

Details

First, please take a look at the previous installment Lightstreamer - "Hello World" Tutorial - HTML Client, which provides some background and the general description of the application. Notice that the front-end will be exactly the same. We created a very simple HTML page that subscribes to the "greetings" item, using the "HELLOWORLD" Adapter. Now, we will replace the "HELLOWORLD" Adapter implementation based on Java with a Python equivalent. On the client side, nothing will change, as server-side Adapters can be transparently switched and changed, as long as they respect the same interfaces. Thanks to this decoupling, provided by Lightstreamer Server, we could even do something different. For example, we could keep the Java Adapter on the server side and use C# .net, instead of HTML, on the client side. Or, we could use the Python Adapter on the server side and use Java, instead of HMTL, on the client side. Basically, all the combinations of languages and technologies on the client side and on the server side are supported.

Please refer to General Concepts for more details about Lightstreamer Adapters.

Python Interfaces

Lightstreamer Server exposes native Java Adapter interfaces. The Python interfaces are added through the Lightstreamer Adapter Remoting Infrastructure (ARI). Let's have a look at it.

General architecture

ARI is simply made up of two Proxy Adapters and a Network Protocol. The two Proxy Adapters implement the Java interfaces and are meant to be plugged into Lightstreamer Kernel, exactly as we did for our original "HELLOWORLD" Java Adapter. There are two Proxy Adapters because one implements the Data Adapter interface and the other implements the Metadata Adapter interface. Our "Hello World" example uses a default Metadata Adapter, so we only need the Proxy Data Adapter.

Basically, the Proxy Data Adapter exposes the Data Adapter interface through TCP sockets. In other words, it offers a Network Protocol, which any remote counterpart can implement to behave as a Lightstreamer Data Adapter. This means you can write a remote Data Adapter in any language, provided that you have access to plain TCP sockets. But, if your remote Data Adapter is based on certain languages/technologies (such as Java, .NET, and Node.js), you can forget about direct socket programming, and leverage a ready-made library that exposes a higher level interface. Now, you will simply have to implement this higher level interface. So the Proxy Data Adapter converts from a Java interface to TCP sockets, and the API library converts from TCP sockets to higher level interface.

You may find more details about ARI in Adapter Remoting Infrastructure Network Protocol Specification.

Dig the Code

The Python Data Adapter

All the required Python code is provided by the helloworld.py module.

First, we import the classes included in the lightstreamer-adapter subpackages, required to communicate with the Proxy Adapters:

from lightstreamer_adapter.interfaces.data import DataProvider
from lightstreamer_adapter.server import DataProviderServer

The HelloWorldDataAdapter extends DataProvider abstract class, which is a Python equivalent of the Java DataProvider interface, provided by the Lightstreamer SDK for Python Adapters:

class HelloWorldDataAdapter(DataProvider):

    def __init__(self):
        self.greetings = None
        self.executing = threading.Event()
        self.listener = None

    def initialize(self, parameters, config_file=None):
        pass

    def set_listener(self, event_listener):
        self.listener = event_listener

    def subscribe(self, item_name):
        if item_name == "greetings":
            self.greetings = threading.Thread(target=self.generate_greetings,
                                              name="Greetings")
            self.greetings.start()

    def unsubscribe(self, item_name):
        if item_name == "greetings":
            self.executing.clear()
            self.greetings.join()

    def issnapshot_available(self, item_name):
        return False

    def generate_greetings(self):
        random.seed()
        counter = 0
        while not self.executing.is_set():
            events = {"message": 'Hello' if counter % 2 == 0 else 'World',
                      "timestamp": time.strftime("%a, %d %b %Y %H:%M:%S")}
            counter += 1
            self.listener.update("greetings", events, False)
            time.sleep(random.uniform(1, 2))

The Adapter's subscribe method is invoked when a new item is subscribed for the first time. When the "greetings" item is subscribed by the first user, the Greetings thread is started and begins to generate the real-time data, as specified in its target generate_greetings method. If more users subscribe to the "greetings" item, the subscribe method is no longer invoked. When the last user unsubscribes from this item, the Adapter is notified through the unsubscribe invocation. In this case, the Greetings thread is terminated and no more events are published for that item. If a new user re-subscribes to "greetings", the subscribe method is invoked again ad the process resumes the same way.

The final part of the script initializes and activates the communication with the Proxy Adapters:

def main():
    address = ("localhost", 6663)
    data_adapter = HelloWorldDataAdapter()
    dataprovider_server = DataProviderServer(data_adapter, address)
    dataprovider_server.start()


if __name__ == "__main__":
    main()
    shutdown_event = threading.Event()
    shutdown_event.wait()

First, we initialize a tuple with the target address information, which comprise the host of the Lightstreamer Server and the listening TCP port configured for the Proxy Data Adapter (see below). Note: The address structure includes two port members, which, however, can be assigned the same value. After that, we create a DataProviderServer object, passing to it a new HelloWorldAdapter instance and the initialized address.

Finally, we start the DataProviderServer instance and ensure that the main thread stays alive (the latter is needed, since Python 3.9, to allow the SDK library to take advantage of the system's ThreadPoolExecutor class).

The Adapter Set Configuration

This Adapter Set is configured and will be referenced by the clients as PYTHON_HELLOWORLD. For this demo, we configure just the Data Adapter as a Proxy Data Adapter, while instead, as Metadata Adapter, we use the LiteralBasedProvider, a simple full implementation of a Metadata Adapter, already provided by Lightstreamer server. As Proxy Data Adapter, you may configure also the robust versions. The Robust Proxy Data Adapter has some recovery capabilities and avoid to terminate the Lightstreamer Server process, so it can handle the case in which a Remote Data Adapter is missing or fails, by suspending the data flow and trying to connect to a new Remote Data Adapter instance. Full details on the recovery behavior of the Robust Data Adapter are available as inline comments within the provided template.

The adapters.xml file for this demo should look like:

<?xml version="1.0"?>
 
<adapters_conf id="PYTHON_HELLOWORLD">
 
  <metadata_provider>
    <adapter_class>com.lightstreamer.adapters.metadata.LiteralBasedProvider</adapter_class>
  </metadata_provider>
 
  <data_provider>
    <adapter_class>PROXY_FOR_REMOTE_ADAPTER</adapter_class>
    <classloader>log-enabled</classloader>
    <param name="request_reply_port">6663</param>
  </data_provider>
 
</adapters_conf>

NOTE: not all configuration options of a Proxy Adapter are exposed by the file suggested above.
You can easily expand your configurations using the generic template for basic and robust Proxy Adapters as a reference.

Install

If you want to install a version of this demo in your local Lightstreamer Server, follow these steps:

  • Download Lightstreamer Server (Lightstreamer Server comes with a free non-expiring demo license for 20 connected users) from Lightstreamer Download page, and install it, as explained in the GETTING_STARTED.TXT file in the installation home directory.

  • Get the deploy.zip file of the latest " release and unzip it, obtaining the deployment folder.

  • Plug the Proxy Data Adapter into the Server: go to the Deployment_LS folder and copy the PythonHelloWorld directory and all of its files into the adapters folder of your Lightstreamer Server installation.

  • Alternatively, you may plug the robust versions of the Proxy Data Adapter: go to the Deployment_LS(robust) folder and copy the PythonHelloWorld directory and all of its files into the adapters folder.

  • Install the Lightstreamer SDK for Python Adapter package, by launching the command:

    $ pip install --pre lightstreamer-adapter
  • Download the helloworld.py file from this project.

  • Launch Lightstreamer Server. The Server startup will complete only after a successful connection between the Proxy Data Adapter and the Remote Data Adapter.

  • Launch the Python Remote Adapter, through the command:

    $ python helloworld.py
  • Test the Adapter, launching the "Hello World" Tutorial - HTML Client listed in Clients Using This Adapter.

    • To make the "Hello World" Tutorial - HTML Client front-end pages get data from the newly installed Adapter Set, you need to modify the front-end pages and set the required Adapter Set name to PYTHON_HELLOWORLD, when creating the LightstreamerClient instance. So edit the index.html page of the Hello World front-end, deployed under Lightstreamer/pages/HelloWorld, and replace:

      var client = new LightstreamerClient(null, "HELLOWORLD");

      with:

      var client = new LightstreamerClient(null, "PYTHON_HELLOWORLD");
    • Open a browser window and go to: http://localhost:8080/HelloWorld/

See Also

Clients Using This Adapter

Related Projects

Lightstreamer Compatibility Notes

  • Compatible with Lightstreamer SDK for Python Adapters version 1.3 or newer and Lightstreamer Server version 7.4 or newer.
  • For a version of this example compatible with Lightstreamer Server version since 6.0, please refer to this tag.
  • For a version of this example compatible with Lightstreamer SDK for Python Adapters version 1.0 to 1.2, please refer to this tag.

Final Notes

Please post to our support forums any feedback or question you might have. Thanks!