---------------------
*UPDATE*
Lightstreamer Server v.7.4.0 introduced official support for request-responde interactions based on the sendMessage facility. Please see this announcement for more information.
---------------------
Lightstreamer originated for asynchronous pub/sub based interaction. But there are cases where using Lightstreamer also for traditional synchronous request/response interaction would be handy. Out-of-the-box support for request/response (aka RPC) will be offered in a future version of Lightstreamer. In the meantime, there are two different ways to easily achieve request/response in Lightstreamer.
Let's see both the solutions through an example.
The request is an integer number and the answer will be the sum of that number with the username (the username is a number too). Most of the error handling is obviously missing to keep the code as small as possible.
SOLUTION 1
A disposable subscription is used. It will carry the request in the item name and the answer in its only update (snapshot). Once the response is received, the subscription will be unsubscribed.
JS Client:[SYNTAX=JS]var userName = Math.round(Math.random()*100);
console.log("user: " + userName);
require(["LightstreamerClient","Subscription"], function(LightstreamerClient,Subscription) {
var client = new LightstreamerClient("http://localhost:8080","MY_ADAPTERS");
client.connectionDetails.setUser(userName);
client.connect();
var requestSubscription = new Subscription("MERGE","REQ:42",["response"]);
requestSubscription.setRequestedSnapshot("yes");
requestSubscription.addListener({
onItemUpdate: function(update) {
console.log("response: " + update.getValue("response"));
client.unsubscribe(requestSubscription);
}
});
client.subscribe(requestSubscription);
});[/SYNTAX]
Server (Java Metadata Adapter):[SYNTAX=JAVA]public class MyMetadataAdapter extends LiteralBasedProvider {
public String[] getItems(String user, String sessionID, String group) throws ItemsException {
String[] split = super.getItems(user,sessionID,group);
for (int i = 0; i<split.length; i++) {
if (split
.startsWith("REQ:")) {
split = "RESP:"+user+":"+split.substring(4);
} else if (group.startsWith("RESP:")) {
// protection from unauthorized use of user-specific items
throw new ItemsException("Unexpected item name");
}
}
return split;
}
}[/SYNTAX]
Server (Java Data Adapter):[SYNTAX=JAVA]public class MyDataAdapter implements SmartDataProvider {
private ItemEventListener listener;
@Override
public void setListener(ItemEventListener listener) {
this.listener = listener;
}
@Override
public void subscribe(String itemName, Object itemHandle, boolean arg2)
throws SubscriptionException, FailureException {
String[] request = itemName.split(":");
if (!request[0].equals("RESP")) {
throw new SubscriptionException("No such item");
}
//in this simple example the response is the request (request[2]) + the username (request[1])
//if obtaining the answer is actually a long process this code should be executed in a different thread
Map<String,String> resp = new HashMap<String,String>();
int sum = Integer.valueOf(request[1])+Integer.valueOf(request[2]);
resp.put("response", String.valueOf(sum));
this.listener.smartUpdate(itemHandle, resp, true);
}
[cut]
}[/SYNTAX]
SOLUTION 2
This is a trick that exploits the capability of the sendMessage call to receive error messages from the Metadata Adapter. This solution can only be used if the notifyUserMessage method in the Metadata Adapter can actually execute fast.
JS Client:
[SYNTAX=JS]var userName = Math.round(Math.random()*100);
console.log("user: " + userName);
require(["LightstreamerClient","Subscription"], function(LightstreamerClient,Subscription) {
var client = new LightstreamerClient("http://localhost:8080","MY_ADAPTERS");
client.connectionDetails.setUser(userName);
client.connect();
client.addListener({
onStatusChange:function(newStatus) {
if (newStatus.indexOf("CONNECTED:") == 0) {
//in this example code we send the request here to be certain to be connected to the LS server
//doing so actually makes the client send more requests, thus running this code will result
//in more than one request/response interaction
client.sendMessage("42",null,null,{
onDeny: function(originalMex, code, resp) {
console.log("response: "+resp);
}
});
}
}
});
});[/SYNTAX]
Server (Java Metadata Adapter)
[SYNTAX=JAVA]public class MyMetadataAdapter extends LiteralBasedProvider {
public void notifyUserMessage(String user, String sessionID, String message)
throws CreditsException, NotificationException {
//if it takes time to obtain the answer to the request this
//approach should not be used
int res = Integer.valueOf(message) + Integer.valueOf(user);
throw new CreditsException(-1,String.valueOf(res));
}
}[/SYNTAX]