PDA

View Full Version : Request-Response APIs


sundaramlaxman
02-19-2007, 05:20 PM
Does Lightstreamer have any Request-Response APIs? We want to query the data-provider via the lightstream server for some information such as profiles/ portfolios etc. before setting up to recieve streaming data; the sendMessage API seems to only return true or false.

Mone
02-20-2007, 09:59 AM
Hi,

Lightstreamer’s paradigm and architecture is specialized for Publish/Subscribe, rather than Request/Response, because it is usually added to an existing application (web-based or thick) that already has an application server that delivers the “pulled” data.
That said, it is possible to use Lightstreamer in a Request/Response fashion through one of the following techniques:
1#
- subscribe to specific items (e.g. profiles, portfolios) [= Request]
- receive the data snapshot [= Response]
- unsubscribe to that item after the snapshot has been received
The Data Adapter will have to retrieve the right data according to the item names (or more precisely
group names).

2#
- subscribe an user-related item
- send requests through sendMessage (http://www.lightstreamer.com/docs/client_web_jsdoc/LightstreamerEngine.html#sendMessage) [= Request]
- receive response on the streaming connection [= Response]
The MetaData Adapter receives messages via notifyUserMessage (http://www.lightstreamer.com/docs/adapter_java_javadoc/com/lightstreamer/interfaces/metadata/MetadataProvider.html#notifyUserMessage%28java.lang.String,%20java.lang.String,%20java.lang.String%2 9) and communicates with the Data Adapter (http://www.lightstreamer.com/vb/showthread.php?t=51) to send the right response back.

Alessandro
02-20-2007, 07:31 PM
Due to the growing popularity of that feature request, we are thinking about adding a ready-made Request/Response mechanism in a future version of Lightstreamer.
In the meanhwile, the two options outlined by Mone work great.

Mone
02-21-2007, 03:04 PM
1#
- subscribe to specific items (e.g. profiles, portfolios) [= Request]
- receive the data snapshot [= Response]
- unsubscribe to that item after the snapshot has been received
The Data Adapter will have to retrieve the right data according to the item names (or more precisely
group names).

I want to expand on this topic through a simple example.
We will see a DataAdapter that responds to our requests with a list of 5 elements. Each element of the list has 2 fields, "key" that contains the name of the subscribed item plus a progressive and value that contains the string "Value" plus the same progressive.

First of all we compose the adapters.xml file for the adapter. We will use the LiteralBasedProvider as MetaDataProvider and our custom class FakeSubAdapter as DataProvider.
Moreover we configure the LiterBasedProvider with some parameters:
distinct_snapshot_length - is the maximum number of snapshot events available for each item of the adapter. This is the value returned by the getDistinctSnapshotLength (file:///C:/Programmi/Lightstreamer/sdk_for_adapters/examples/ReusableMetadataAdapters/doc/LS_Generic_Adapters_Javadoc/com/lightstreamer/interfaces/metadata/MetadataProvider.html#getDistinctSnapshotLength%28java.lang.String%29) method; implementing your own MetaDataProvider this value could be different per each item.
item_family_1 - is used as a Pattern to group items inside a family. Using .* we group in the same family all possible items.
modes_for_item_family_1 - lists the admitted subscription modes for items pertaining to item_family_1. We admit only DISTINCT subscriptions.This is the adapters.xml file
<?xml version="1.0"?>

<adapters_conf id="FAKESUB">
<metadata_provider>
<adapter_class>com.lightstreamer.adapters.metadata.LiteralBasedProvider</adapter_class>
<param name="distinct_snapshot_length">5</param>
<param name="item_family_1">.*</param>
<param name="modes_for_item_family_1">DISTINCT</param>
</metadata_provider>

<data_provider>
<adapter_class>com.lightstreamer.req_resp_examples.fake_sub.FakeSubAdapter</adapter_class>
</data_provider>
</adapters_conf>

This is the DataProvider (http://www.lightstreamer.com/docs/adapter_java_javadoc/com/lightstreamer/interfaces/data/DataProvider.html) implementation (refer to comments inside the code for the explanations on how it works):
package com.lightstreamer.req_resp_examples.fake_sub;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import com.lightstreamer.interfaces.data.DataProvider;
import com.lightstreamer.interfaces.data.DataProviderException;
import com.lightstreamer.interfaces.data.FailureException;
import com.lightstreamer.interfaces.data.ItemEventListener;
import com.lightstreamer.interfaces.data.SubscriptionException;

public class FakeSubAdapter implements DataProvider {

private HashMap responseThreads = new HashMap();
private ItemEventListener listener;

public void init(Map params, File configDir) throws DataProviderException {
return;
}

public void setListener(ItemEventListener listener) {
//set the listener that will receive the updates
this.listener = listener;
}

public void subscribe(String itemName, boolean needsIterator) throws SubscriptionException, FailureException {
//create the thread that will handle the response. In a production
//scenario you would probably use a pool, but for this example
//create a new thread per each subscription is enough
ResponseThread rt = new ResponseThread(itemName);
//take trace of each subscribtion-related thread
responseThreads.put(itemName,rt);
//starts the thread that will send the response to clients
rt.start();
}

public void unsubscribe(String itemName) throws SubscriptionException, FailureException {
ResponseThread rt = (ResponseThread) responseThreads.get(itemName);
if (rt != null) {
//remove from subscription-related threads list
responseThreads.remove(itemName);
//if the thread is sending something (ie it has not finished to send the snapshot
//that is our response) we stop it
rt.end();
}
}

public boolean isSnapshotAvailable(String itemName) throws SubscriptionException {
//in this case we send ONLY the snapshot, so it is obviously always available
return true;
}

public class ResponseThread extends Thread {

private String itemName;
private volatile boolean exit = false;


public ResponseThread(String itemName) {
//save the itemName, we will use this name as part of the
//updates
this.itemName = itemName;
}

public void run() {
//the response will be a DISTINCT table with 5 rows
for(int i=1; i<=5 && !this.exit; i++) {
//compose a Map to be passed to Lightstreamer kernel
HashMap row = new HashMap();
row.put("key",this.itemName + "->" +String.valueOf(i));
row.put("value","Value"+i);
//pass the map to Lightstreamer kernel. This is an update.
if (!this.exit)listener.update(this.itemName,row,true);
}
//send the endOfSnapshot signal (ie response is finished)
if (!this.exit)listener.endOfSnapshot(this.itemName);
}

public void end() {
//set a flag to stop updates
this.exit = true;
}

}

}

To be continued...

Mone
02-21-2007, 03:11 PM
...continue

Now we need a client to test it, we implement a simple client with the web SDK.
In this page we put a <div> that is filled with the "response" and a "response is complete" message. There is also a form, fill the input field and press submit to make a request to the server. NOTE that since LiteralBasedProvider is used as MetaDataProvider, and since the demo uses the string inside the inputField as a group string, if you try to send a reuqest like "something somethingElse" you subscribe 2 items (in this case 2 "requests").
The page is made to be deployed under Lightstreamer internal web server so domain host and port are set to null.
To gather the desired data we use a NonVisualTable (http://www.lightstreamer.com/docs/client_web_jsdoc/NonVisualTable.html). Refer to the comments inside the code for further explanations

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<!-- inclusion of Lightstreamer web client library-->
<script src="/LS/lscommons.js"></script>
<script src="/LS/lspushpage.js"></script>
</head>

<body>
<!--this div will show the response-->
<div id="response"></div>

<!--this form is used to call a method that makes the requests-->
<form onsubmit="makeRequest();return false;">
<input id="requestQuery" type="text" />
<input type="submit" value="send request" />
</form>
<script>


/////////////////PushPage Configuration
lsPage = new PushPage();

lsPage.context.setDomain(null);

lsPage.onEngineReady = function(lsEngine) {
lsEngine.connection.setLSHost(null);
lsEngine.connection.setLSPort(null);
lsEngine.connection.setAdapterName("FAKESUB");
lsEngine.changeStatus("STREAMING");
}

lsPage.bind();
lsPage.loadEngineMinimal("/LS/lsengine.html");

/////////////////////////////////Request/Response handler
var responseDiv = document.getElementById("response");
var requestQuery = document.getElementById("requestQuery");

function makeRequest() {
//this is the string of the input field
var request = requestQuery.value;
//create a NonVisualTable, the item is the string of the input field, as fields
//"key" and "value" and subscribe in DISTINCT mode
var table = new NonVisualTable(request, new Array("key","value"), "DISTINCT");
//we need just the snapshot
table.setSnapshotRequired(true);
//this callback is called after subscription notification. We clear the responseDiv
//so our response will be the only one inside it
table.onStart = function() {
responseDiv.innerHTML = "";
}
//this callback is called per each received update. We compose a string with
//the "key" and "value" fields and append this string to the responseDiv
table.onItemUpdate = function(itemPos, itemObj, itemName) {
var k = itemObj.getNewValue("key");
var v = itemObj.getNewValue("value");
responseDiv.innerHTML += k + ": " + v +"<br/>";
}
//this callback is called when all snapshot events have been received. Put
//this info in the responseDiv and remove the subscription
table.onEndOfSnapshot = function(itemPos,itemName) {
responseDiv.innerHTML += "Response COMPLETE " +itemPos+"<br/>";
//until we keep the table subscribed the data adapter will not be
//called for others clients making the same request (ie Lightstreamer
//kernel will handle directly responses for those clients). In this case
//we don't remove the subscription.
//lsPage.removeTable("req");
}
//this callback is called in case some updates were lost. Put the info inside
//the responseDiv
table.onLostUpdates = function(itemPos,lostUpdates,itemName) {
responseDiv.innerHTML += "We've lost updates!<br/>";
}

//subscribe the table. In this demo we always use the same id ("req") to avoid
//multiple concurrent requests.
lsPage.addTable(table, "req");
}
</script>
</body>

</html>


The sources of the demo are also available as a downloadable zip attacched to this post.