14package org.dartlang.vm.service;
16import com.google.common.collect.Maps;
17import com.google.gson.JsonElement;
18import com.google.gson.JsonObject;
19import com.google.gson.JsonParser;
20import de.roderick.weberknecht.WebSocket;
21import de.roderick.weberknecht.WebSocketEventHandler;
22import de.roderick.weberknecht.WebSocketException;
23import de.roderick.weberknecht.WebSocketMessage;
24import org.dartlang.vm.service.consumer.*;
25import org.dartlang.vm.service.element.*;
26import org.dartlang.vm.service.internal.RequestSink;
27import org.dartlang.vm.service.internal.VmServiceConst;
28import org.dartlang.vm.service.internal.WebSocketRequestSink;
29import org.dartlang.vm.service.logging.Logging;
31import java.io.IOException;
33import java.net.URISyntaxException;
34import java.util.ArrayList;
37import java.util.concurrent.CountDownLatch;
38import java.util.concurrent.TimeUnit;
39import java.util.concurrent.atomic.AtomicInteger;
44@SuppressWarnings({
"unused",
"WeakerAccess"})
51 public static VmService
connect(
final String url)
throws IOException {
56 }
catch (URISyntaxException e) {
57 throw new IOException(
"Invalid URL: " + url, e);
59 String wsScheme = uri.getScheme();
61 throw new IOException(
"Unsupported URL scheme: " + wsScheme);
67 webSocket =
new WebSocket(uri);
68 }
catch (WebSocketException e) {
69 throw new IOException(
"Failed to create websocket: " + url, e);
71 final VmService vmService =
new VmService();
74 webSocket.setEventHandler(
new WebSocketEventHandler() {
76 public void onClose() {
77 Logging.getLogger().logInformation(
"VM connection closed: " + url);
79 vmService.connectionClosed();
83 public void onMessage(WebSocketMessage
message) {
84 Logging.getLogger().logInformation(
"VM message: " +
message.getText());
86 vmService.processMessage(
message.getText());
87 }
catch (Exception e) {
88 Logging.getLogger().logError(e.getMessage(), e);
93 public void onOpen() {
94 vmService.connectionOpened();
96 Logging.getLogger().logInformation(
"VM connection open: " + url);
100 public void onPing() {
104 public void onPong() {
112 }
catch (WebSocketException e) {
113 throw new IOException(
"Failed to connect: " + url, e);
114 }
catch (ArrayIndexOutOfBoundsException e) {
117 throw new IOException(
"Failed to connect: " + url, e);
119 vmService.requestSink =
new WebSocketRequestSink(webSocket);
122 final CountDownLatch latch =
new CountDownLatch(1);
123 final String[] errMsg =
new String[1];
124 vmService.getVersion(
new VersionConsumer() {
126 public void onError(RPCError
error) {
127 String msg =
"Failed to determine protocol version: " +
error.getCode() +
"\n message: "
128 +
error.getMessage() +
"\n details: " +
error.getDetails();
129 Logging.getLogger().logInformation(msg);
134 public void received(
Version version) {
135 vmService.runtimeVersion = version;
142 if (!latch.await(5, TimeUnit.SECONDS)) {
143 throw new IOException(
"Failed to determine protocol version");
145 if (errMsg[0] !=
null) {
146 throw new IOException(errMsg[0]);
148 }
catch (InterruptedException e) {
149 throw new RuntimeException(
"Interrupted while waiting for response", e);
164 return connect(
"ws://localhost:" + port +
"/ws");
171 private final Map<String, Consumer> consumerMap = Maps.newHashMap();
176 private final Object consumerMapLock =
new Object();
181 private final AtomicInteger nextId =
new AtomicInteger();
191 private final Map<String, RemoteServiceRunner> remoteServiceRunners = Maps.newHashMap();
204 vmListeners.
add(listener);
211 vmListeners.remove(listener);
218 remoteServiceRunners.put(service, runner);
225 remoteServiceRunners.remove(service);
232 return runtimeVersion;
245 public void getInstance(String isolateId, String instanceId,
final GetInstanceConsumer consumer) {
246 getObject(isolateId, instanceId,
new GetObjectConsumer() {
249 public void onError(RPCError
error) {
250 consumer.onError(
error);
254 public void received(Obj response) {
255 if (response instanceof Instance) {
256 consumer.received((Instance) response);
258 onError(RPCError.unexpected(
"Instance", response));
263 public void received(Sentinel response) {
264 onError(RPCError.unexpected(
"Instance", response));
272 public void getLibrary(String isolateId, String libraryId,
final GetLibraryConsumer consumer) {
273 getObject(isolateId, libraryId,
new GetObjectConsumer() {
276 public void onError(RPCError
error) {
277 consumer.onError(
error);
281 public void received(Obj response) {
282 if (response instanceof Library) {
283 consumer.received((Library) response);
285 onError(RPCError.unexpected(
"Library", response));
290 public void received(Sentinel response) {
291 onError(RPCError.unexpected(
"Library", response));
296 public abstract void getObject(String isolateId, String objectId, GetObjectConsumer consumer);
304 JsonObject
params =
new JsonObject();
305 params.addProperty(
"isolateId", isolateId);
306 request(method,
params, consumer);
315 params.addProperty(
"isolateId", isolateId);
316 request(method,
params, consumer);
322 protected void request(String method, JsonObject
params, Consumer consumer) {
325 String
id = Integer.toString(nextId.incrementAndGet());
326 JsonObject request =
new JsonObject();
328 request.addProperty(JSONRPC, JSONRPC_VERSION);
329 request.addProperty(ID,
id);
330 request.addProperty(METHOD, method);
331 request.add(PARAMS,
params);
334 synchronized (consumerMapLock) {
335 consumerMap.put(
id, consumer);
339 requestSink.add(request);
345 listener.connectionOpened();
346 }
catch (Exception e) {
347 Logging.getLogger().logError(
"Exception notifying listener", e);
352 private void forwardEvent(String streamId,
Event event) {
355 listener.received(streamId,
event);
356 }
catch (Exception e) {
357 Logging.getLogger().logError(
"Exception processing event: " + streamId +
", " +
event.getJson(), e);
365 listener.connectionClosed();
366 }
catch (Exception e) {
367 Logging.getLogger().logError(
"Exception notifying listener", e);
375 Class<? extends Consumer> consumerClass = consumer.getClass();
376 StringBuilder msg =
new StringBuilder();
377 msg.append(
"Expected response for ").append(consumerClass).append(
"\n");
378 for (Class<?> interf : consumerClass.getInterfaces()) {
379 msg.append(
" implementing ").append(interf).append(
"\n");
381 msg.append(
" but received ").append(json);
382 Logging.getLogger().logError(msg.toString());
390 if (jsonText ==
null || jsonText.isEmpty()) {
397 json = (JsonObject)
new JsonParser().parse(jsonText);
398 }
catch (Exception e) {
399 Logging.getLogger().logError(
"Parse message failed: " + jsonText, e);
403 if (json.has(
"method")) {
404 if (!json.has(PARAMS)) {
405 final String
message =
"Missing " + PARAMS;
406 Logging.getLogger().logError(
message);
407 final JsonObject response =
new JsonObject();
408 response.addProperty(JSONRPC, JSONRPC_VERSION);
409 final JsonObject
error =
new JsonObject();
410 error.addProperty(CODE, INVALID_REQUEST);
413 requestSink.add(response);
416 if (json.has(
"id")) {
417 processRequest(json);
419 processNotification(json);
421 }
else if (json.has(
"result") || json.has(
"error")) {
422 processResponse(json);
424 Logging.getLogger().logError(
"Malformed message");
429 final JsonObject response =
new JsonObject();
430 response.addProperty(JSONRPC, JSONRPC_VERSION);
435 id = json.get(ID).getAsString();
436 }
catch (Exception e) {
437 final String
message =
"Request malformed " + ID;
438 Logging.getLogger().logError(
message, e);
439 final JsonObject
error =
new JsonObject();
440 error.addProperty(CODE, INVALID_REQUEST);
443 requestSink.add(response);
447 response.addProperty(ID,
id);
451 method = json.get(METHOD).getAsString();
452 }
catch (Exception e) {
453 final String
message =
"Request malformed " + METHOD;
454 Logging.getLogger().logError(
message, e);
455 final JsonObject
error =
new JsonObject();
456 error.addProperty(CODE, INVALID_REQUEST);
459 requestSink.add(response);
465 params = json.get(PARAMS).getAsJsonObject();
466 }
catch (Exception e) {
467 final String
message =
"Request malformed " + METHOD;
468 Logging.getLogger().logError(
message, e);
469 final JsonObject
error =
new JsonObject();
470 error.addProperty(CODE, INVALID_REQUEST);
473 requestSink.add(response);
477 if (!remoteServiceRunners.containsKey(method)) {
478 final String
message =
"Unknown service " + method;
479 Logging.getLogger().logError(
message);
480 final JsonObject
error =
new JsonObject();
481 error.addProperty(CODE, METHOD_NOT_FOUND);
484 requestSink.add(response);
493 requestSink.add(response);
496 public void error(
int code, String
message, JsonObject data) {
497 final JsonObject
error =
new JsonObject();
498 error.addProperty(CODE, code);
501 error.add(DATA, data);
504 requestSink.add(response);
507 }
catch (Exception e) {
508 final String
message =
"Internal Server Error";
509 Logging.getLogger().logError(
message, e);
510 final JsonObject
error =
new JsonObject();
511 error.addProperty(CODE, SERVER_ERROR);
514 requestSink.add(response);
524 public void error(
int code, String
message, JsonObject data) {
532 method = json.get(METHOD).getAsString();
533 }
catch (Exception e) {
534 Logging.getLogger().logError(
"Request malformed " + METHOD, e);
539 params = json.get(PARAMS).getAsJsonObject();
540 }
catch (Exception e) {
541 Logging.getLogger().logError(
"Event missing " + PARAMS, e);
544 if (
"streamNotify".
equals(method)) {
547 streamId =
params.get(STREAM_ID).getAsString();
548 }
catch (Exception e) {
549 Logging.getLogger().logError(
"Event missing " + STREAM_ID, e);
554 event =
new Event(
params.get(EVENT).getAsJsonObject());
555 }
catch (Exception e) {
556 Logging.getLogger().logError(
"Event missing " + EVENT, e);
559 forwardEvent(streamId,
event);
561 if (!remoteServiceRunners.containsKey(method)) {
562 Logging.getLogger().logError(
"Unknown service " + method);
569 }
catch (Exception e) {
570 Logging.getLogger().logError(
"Internal Server Error", e);
576 return str.replaceAll(
"\r\n",
" ").replaceAll(
"\n",
" ");
580 JsonElement idElem = json.get(ID);
581 if (idElem ==
null) {
582 Logging.getLogger().logError(
"Response missing " + ID);
589 id = idElem.getAsString();
590 }
catch (Exception e) {
591 Logging.getLogger().logError(
"Response missing " + ID, e);
594 Consumer consumer = consumerMap.remove(
id);
595 if (consumer ==
null) {
596 Logging.getLogger().logError(
"No consumer associated with " + ID +
": " +
id);
601 JsonElement resultElem = json.get(
RESULT);
602 if (resultElem !=
null) {
605 result = resultElem.getAsJsonObject();
606 }
catch (Exception e) {
607 Logging.getLogger().logError(
"Response has invalid " +
RESULT, e);
610 String responseType =
"";
612 responseType =
result.get(
TYPE).getAsString();
615 else if (!(consumer instanceof ServiceExtensionConsumer)) {
616 Logging.getLogger().logError(
"Response missing " +
TYPE +
": " +
result.toString());
619 forwardResponse(consumer, responseType,
result);
624 resultElem = json.get(
ERROR);
625 if (resultElem !=
null) {
628 error = resultElem.getAsJsonObject();
629 }
catch (Exception e) {
630 Logging.getLogger().logError(
"Response has invalid " +
RESULT, e);
633 consumer.onError(
new RPCError(
error));
637 Logging.getLogger().logError(
"Response missing " +
RESULT +
" and " +
ERROR);
static bool equals(T *a, T *b)
void add(sk_sp< SkIDChangeListener > listener) SK_EXCLUDES(fMutex)
void addServiceRunner(String service, RemoteServiceRunner runner)
void processNotification(JsonObject json)
void processResponse(JsonObject json)
String removeNewLines(String str)
void getLibrary(String isolateId, String libraryId, final GetLibraryConsumer consumer)
static VmService connect(final String url)
void logUnknownResponse(Consumer consumer, JsonObject json)
void request(String method, JsonObject params, Consumer consumer)
void removeServiceRunner(String service)
void callServiceExtension(String isolateId, String method, JsonObject params, ServiceExtensionConsumer consumer)
void processRequest(JsonObject json)
void addVmServiceListener(VmServiceListener listener)
void processMessage(String jsonText)
Version getRuntimeVersion()
abstract void getObject(String isolateId, String objectId, GetObjectConsumer consumer)
void getInstance(String isolateId, String instanceId, final GetInstanceConsumer consumer)
static VmService localConnect(int port)
void callServiceExtension(String isolateId, String method, ServiceExtensionConsumer consumer)
abstract void forwardResponse(Consumer consumer, String type, JsonObject json)
void removeVmServiceListener(VmServiceListener listener)
const EmbeddedViewParams * params
const uint8_t uint32_t uint32_t GError ** error
void run(JsonObject params, RemoteServiceCompleter completer)
const CatchEntryMove de[]