org.dsa.iot.dslink.link.Requester Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dslink Show documentation
Show all versions of dslink Show documentation
SDK for the IoT DSA protocol
package org.dsa.iot.dslink.link;
import org.dsa.iot.dslink.DSLink;
import org.dsa.iot.dslink.DSLinkHandler;
import org.dsa.iot.dslink.methods.Request;
import org.dsa.iot.dslink.methods.StreamState;
import org.dsa.iot.dslink.methods.requests.*;
import org.dsa.iot.dslink.methods.responses.*;
import org.dsa.iot.dslink.node.Node;
import org.dsa.iot.dslink.node.NodeManager;
import org.dsa.iot.dslink.node.NodePair;
import org.dsa.iot.dslink.node.SubscriptionManager;
import org.dsa.iot.dslink.node.value.SubscriptionValue;
import org.dsa.iot.dslink.util.Objects;
import org.dsa.iot.dslink.util.SubData;
import org.dsa.iot.dslink.util.handler.Handler;
import org.dsa.iot.dslink.util.json.JsonObject;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Handles incoming responses and outgoing requests.
*
* @author Samuel Grenier
*/
public class Requester extends Linkable {
private final Map reqs;
/**
* Current request ID to send to the client
*/
private final AtomicInteger currentReqID = new AtomicInteger();
/**
* Current subscription ID to send to the client
*/
private final AtomicInteger currentSubID = new AtomicInteger();
/**
* Mapping of path->sid
*/
private final Map subPaths = new ConcurrentHashMap<>();
/**
* Mapping of sid->path
*/
private final Map subSids = new ConcurrentHashMap<>();
/**
* Mapping of sid->handler
*/
private final Map> subUpdates = new ConcurrentHashMap<>();
/**
* Mapping of rid->response
*/
private final Map invokeResponses = new HashMap<>();
/**
* Constructs a requester
*
* @param handler Handler for callbacks and data handling
*/
public Requester(DSLinkHandler handler) {
super(handler);
reqs = new ConcurrentHashMap<>();
}
@SuppressWarnings("unused")
public Map getSubscriptionPaths() {
return Collections.unmodifiableMap(subPaths);
}
public Map getSubscriptionIDs() {
return Collections.unmodifiableMap(subSids);
}
@SuppressWarnings("unused")
public boolean isSubscribed(String path) {
return subPaths.containsKey(path);
}
public Map> getSubscriptionHandlers() {
return Collections.unmodifiableMap(subUpdates);
}
public void subscribe(String path,
Handler onUpdate) {
SubData sub = new SubData(path, null);
subscribe(sub, onUpdate);
}
public void subscribe(SubData path,
Handler onUpdate) {
subscribe(Collections.singleton(path), onUpdate);
}
public void subscribe(Set paths,
Handler onUpdate) {
if (paths == null) {
throw new NullPointerException("paths");
}
subscribe(new SubscribeRequest(paths), onUpdate);
}
public void subscribe(SubscribeRequest req,
Handler onUpdate) {
if (req == null) {
throw new NullPointerException("req");
}
final Set paths = req.getPaths();
Map subs = new HashMap<>();
int min = currentSubID.getAndAdd(paths.size());
int max = min + paths.size();
Iterator it = paths.iterator();
StringBuilder error = null;
while (min < max && it.hasNext()) {
try {
SubData data = it.next();
String path = data.getPath();
subs.put(data, min);
Integer prev = subPaths.put(path, min);
if (prev != null) {
String err = "Path " + path + " already subscribed";
throw new RuntimeException(err);
}
subSids.put(min, path);
if (onUpdate != null) {
subUpdates.put(min, onUpdate);
}
min++;
} catch (IllegalArgumentException e) {
if (error == null) {
error = new StringBuilder();
}
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
error.append(writer.toString());
error.append("\n\n");
}
}
req.setSubSids(subs);
RequestWrapper wrapper = new RequestWrapper(req);
sendRequest(wrapper, currentReqID.incrementAndGet());
if (error != null) {
throw new RuntimeException(error.toString());
}
}
public void unsubscribe(String path, Handler onResponse) {
Set paths = new HashSet<>();
paths.add(path);
unsubscribe(paths, onResponse);
}
public void unsubscribe(Set paths, Handler onResponse) {
if (paths == null) {
throw new NullPointerException("paths");
}
List subs = new ArrayList<>();
for (String path : paths) {
path = NodeManager.normalizePath(path, true);
Integer sid = subPaths.remove(path);
if (sid != null) {
subs.add(sid);
subSids.remove(sid);
subUpdates.remove(sid);
}
}
UnsubscribeRequest req = new UnsubscribeRequest(subs);
RequestWrapper wrapper = new RequestWrapper(req);
wrapper.setUnsubHandler(onResponse);
sendRequest(wrapper, currentReqID.incrementAndGet());
}
/**
* Sends a request to the responder to close the given stream.
*
* @param rid Stream to close.
* @param onResponse Response.
*/
@SuppressWarnings("unused")
public void closeStream(int rid, Handler onResponse) {
CloseRequest req = new CloseRequest();
RequestWrapper wrapper = new RequestWrapper(req);
wrapper.setCloseHandler(onResponse);
sendRequest(wrapper, rid);
reqs.remove(rid);
if (onResponse != null) {
wrapper.getCloseHandler().handle(new CloseResponse(rid, null));
}
}
/**
* Sends an invocation request.
*
* @param request Invocation request.
* @param onResponse Response.
*/
public void invoke(InvokeRequest request, Handler onResponse) {
RequestWrapper wrapper = new RequestWrapper(request);
wrapper.setInvokeHandler(onResponse);
sendRequest(wrapper);
}
/**
* Sends a list request.
*
* @param request List request.
* @param onResponse Response.
*/
public void list(ListRequest request, Handler onResponse) {
RequestWrapper wrapper = new RequestWrapper(request);
wrapper.setListHandler(onResponse);
sendRequest(wrapper);
}
/**
* Sends a set request.
*
* @param request Set request.
* @param onResponse Response.
*/
public void set(SetRequest request, Handler onResponse) {
RequestWrapper wrapper = new RequestWrapper(request);
wrapper.setSetHandler(onResponse);
sendRequest(wrapper);
}
/**
* Sends a remove request.
*
* @param request Remove request.
* @param onResponse Called when a response is received.
*/
public void remove(RemoveRequest request, Handler onResponse) {
RequestWrapper wrapper = new RequestWrapper(request);
wrapper.setRemoveHandler(onResponse);
sendRequest(wrapper);
}
/**
* Sends a request to the client.
*
* @param wrapper Request to send to the client.
*/
private void sendRequest(RequestWrapper wrapper) {
int rid = currentReqID.incrementAndGet();
sendRequest(wrapper, rid);
}
/**
* Sends a request to the client with a given request ID.
*
* @param wrapper Request to send to the client
* @param rid Request ID to use
*/
private void sendRequest(RequestWrapper wrapper, int rid) {
final DSLink link = getDSLink();
if (link == null) {
return;
}
Request request = wrapper.getRequest();
JsonObject obj = new JsonObject();
request.addJsonValues(obj);
{
obj.put("rid", rid);
reqs.put(rid, wrapper);
}
obj.put("method", request.getName());
link.getWriter().writeRequest(obj);
}
/**
* Handles incoming responses.
*
* @param in Incoming response.
*/
public void parse(final JsonObject in) {
DSLink link = getDSLink();
if (link == null) {
return;
}
int rid = in.get("rid");
if (rid == 0) {
final SubscriptionUpdate update = new SubscriptionUpdate(this);
Objects.getThreadPool().execute(new Runnable() {
@Override
public void run() {
update.populate(in);
}
});
return;
}
RequestWrapper wrapper = reqs.get(rid);
Request request = wrapper.getRequest();
String method = request.getName();
StreamState stream = StreamState.toEnum((String) in.get("stream"));
if (stream == null) {
stream = StreamState.OPEN;
}
final boolean closed = StreamState.CLOSED == stream;
final NodeManager manager = link.getNodeManager();
switch (method) {
case "list":
ListRequest listRequest = (ListRequest) request;
Node node = manager.getNode(listRequest.getPath(), true).getNode();
String path = node.getPath();
SubscriptionManager subs = link.getSubscriptionManager();
ListResponse resp = new ListResponse(link, subs, rid, node, path);
resp.populate(in);
if (wrapper.getListHandler() != null) {
wrapper.getListHandler().handle(resp);
}
break;
case "set":
SetRequest setRequest = (SetRequest) request;
path = setRequest.getPath();
manager.getNode(path, true);
SetResponse setResponse = new SetResponse(rid, link, path);
setResponse.populate(in);
if (wrapper.getSetHandler() != null) {
wrapper.getSetHandler().handle(setResponse);
}
break;
case "remove":
RemoveRequest removeRequest = (RemoveRequest) request;
NodePair pair = manager.getNode(removeRequest.getPath(), true);
RemoveResponse removeResponse = new RemoveResponse(rid, pair);
removeResponse.populate(in);
if (wrapper.getRemoveHandler() != null) {
wrapper.getRemoveHandler().handle(removeResponse);
}
break;
case "close":
break;
case "subscribe":
SubscribeResponse subResp = new SubscribeResponse(rid, link);
subResp.populate(in);
break;
case "unsubscribe":
UnsubscribeResponse unsubResp = new UnsubscribeResponse(rid, link);
unsubResp.populate(in);
if (wrapper.getUnsubHandler() != null) {
wrapper.getUnsubHandler().handle(unsubResp);
}
break;
case "invoke":
InvokeRequest inReq = (InvokeRequest) request;
path = inReq.getPath();
manager.getNode(path, true);
InvokeResponse inResp;
synchronized (invokeResponses) {
switch (stream) {
case OPEN:
inResp = invokeResponses.get(rid);
break;
case INITIALIZED:
inResp = new InvokeResponse(link, rid, path);
invokeResponses.put(rid, inResp);
break;
case CLOSED:
inResp = invokeResponses.remove(rid);
break;
default:
inResp = null;
}
if (inResp == null) {
inResp = new InvokeResponse(link, rid, path);
}
}
inResp.populate(in);
boolean invoke = false;
if (inReq.waitForStreamClose()) {
if (closed) {
invoke = true;
}
} else {
invoke = true;
}
if (invoke && wrapper.getInvokeHandler() != null) {
wrapper.getInvokeHandler().handle(inResp);
}
break;
default:
throw new RuntimeException("Unsupported method: " + method);
}
if (closed) {
reqs.remove(rid);
}
}
/**
* Forcibly clears all subscriptions and handlers. This does not call
* unsubscribe to the server.
*/
public void clearSubscriptions() {
subPaths.clear();
subSids.clear();
subUpdates.clear();
}
private static class RequestWrapper {
private final Request request;
private Handler closeHandler;
private Handler invokeHandler;
private Handler listHandler;
private Handler removeHandler;
private Handler setHandler;
private Handler unsubHandler;
public RequestWrapper(Request request) {
this.request = request;
}
public Request getRequest() {
return request;
}
public Handler getCloseHandler() {
return closeHandler;
}
public void setCloseHandler(Handler closeHandler) {
this.closeHandler = closeHandler;
}
public Handler getInvokeHandler() {
return invokeHandler;
}
public void setInvokeHandler(Handler invokeHandler) {
this.invokeHandler = invokeHandler;
}
public Handler getListHandler() {
return listHandler;
}
public void setListHandler(Handler listHandler) {
this.listHandler = listHandler;
}
public Handler getRemoveHandler() {
return removeHandler;
}
public void setRemoveHandler(Handler removeHandler) {
this.removeHandler = removeHandler;
}
public Handler getSetHandler() {
return setHandler;
}
public void setSetHandler(Handler setHandler) {
this.setHandler = setHandler;
}
public Handler getUnsubHandler() {
return unsubHandler;
}
public void setUnsubHandler(Handler unsubHandler) {
this.unsubHandler = unsubHandler;
}
}
}