/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.stetho.inspector.jsonrpc;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import java.nio.channels.NotYetConnectedException;
import java.util.HashMap;
import java.util.Map;
import android.database.Observable;
import com.facebook.stetho.inspector.jsonrpc.protocol.JsonRpcRequest;
import com.facebook.stetho.common.Util;
import com.facebook.stetho.json.ObjectMapper;
import com.facebook.stetho.websocket.SimpleSession;
import org.json.JSONObject;
@ThreadSafe
public class JsonRpcPeer {
private final SimpleSession mPeer;
private final ObjectMapper mObjectMapper;
@GuardedBy("this")
private long mNextRequestId;
@GuardedBy("this")
private final Map<Long, PendingRequest> mPendingRequests = new HashMap<>();
private final DisconnectObservable mDisconnectObservable = new DisconnectObservable();
public JsonRpcPeer(ObjectMapper objectMapper, SimpleSession peer) {
mObjectMapper = objectMapper;
mPeer = Util.throwIfNull(peer);
}
public SimpleSession getWebSocket() {
return mPeer;
}
public void invokeMethod(String method, Object paramsObject,
@Nullable PendingRequestCallback callback)
throws NotYetConnectedException {
Util.throwIfNull(method);
Long requestId = (callback != null) ? preparePendingRequest(callback) : null;
// magic, can basically convert anything for some amount of runtime overhead...
JSONObject params = mObjectMapper.convertValue(paramsObject, JSONObject.class);
JsonRpcRequest message = new JsonRpcRequest(requestId, method, params);
String requestString;
JSONObject jsonObject = mObjectMapper.convertValue(message, JSONObject.class);
requestString = jsonObject.toString();
mPeer.sendText(requestString);
}
public void registerDisconnectReceiver(DisconnectReceiver callback) {
mDisconnectObservable.registerObserver(callback);
}
public void unregisterDisconnectReceiver(DisconnectReceiver callback) {
mDisconnectObservable.unregisterObserver(callback);
}
public void invokeDisconnectReceivers() {
mDisconnectObservable.onDisconnect();
}
private synchronized long preparePendingRequest(PendingRequestCallback callback) {
long requestId = mNextRequestId++;
mPendingRequests.put(requestId, new PendingRequest(requestId, callback));
return requestId;
}
public synchronized PendingRequest getAndRemovePendingRequest(long requestId) {
return mPendingRequests.remove(requestId);
}
private static class DisconnectObservable extends Observable<DisconnectReceiver> {
public void onDisconnect() {
for (int i = 0, N = mObservers.size(); i < N; ++i) {
final DisconnectReceiver observer = mObservers.get(i);
observer.onDisconnect();
}
}
}
}