--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/StandaloneVmImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,245 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.StandaloneVm;
+import org.chromium.sdk.UnsupportedVersionException;
+import org.chromium.sdk.internal.protocol.data.ContextHandle;
+import org.chromium.sdk.internal.tools.v8.V8CommandOutput;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.transport.Connection;
+import org.chromium.sdk.internal.transport.Handshaker;
+import org.chromium.sdk.internal.transport.Message;
+import org.chromium.sdk.internal.transport.SocketConnection;
+import org.chromium.sdk.internal.transport.Connection.NetListener;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.ParseException;
+
+/**
+ * Implementation of {@code StandaloneVm}. Currently knows nothing about
+ * contexts, so all existing V8 contexts are presented mixed together.
+ */
+class StandaloneVmImpl extends JavascriptVmImpl implements StandaloneVm {
+
+ /** The class logger. */
+ private static final Logger LOGGER =
+ Logger.getLogger(StandaloneVmImpl.class.getName());
+
+ private static final int WAIT_FOR_HANDSHAKE_TIMEOUT_MS = 3000;
+
+ private static final V8ContextFilter CONTEXT_FILTER = new V8ContextFilter() {
+ public boolean isContextOurs(ContextHandle contextHandle) {
+ // We do not check context in standalone V8 mode.
+ return true;
+ }
+ };
+
+ private final SocketConnection connection;
+ private final Handshaker.StandaloneV8 handshaker;
+
+ private final DebugSession debugSession;
+
+ private DebugEventListener debugEventListener = null;
+ private volatile ConnectionState connectionState = ConnectionState.INIT;
+
+ private volatile Exception disconnectReason = null;
+ private volatile Handshaker.StandaloneV8.RemoteInfo savedRemoteInfo = NULL_REMOTE_INFO;
+
+ private final Object disconnectMonitor = new Object();
+
+ StandaloneVmImpl(SocketConnection connection, Handshaker.StandaloneV8 handshaker) {
+ this.connection = connection;
+ this.handshaker = handshaker;
+ V8CommandOutputImpl v8CommandOutput = new V8CommandOutputImpl(connection);
+ this.debugSession = new DebugSession(sessionManager, CONTEXT_FILTER, v8CommandOutput);
+ }
+
+ public void attach(DebugEventListener listener) throws IOException, UnsupportedVersionException {
+ Exception errorCause = null;
+ try {
+ attachImpl(listener);
+ } catch (IOException e) {
+ errorCause = e;
+ throw e;
+ } catch (UnsupportedVersionException e) {
+ errorCause = e;
+ throw e;
+ } finally {
+ if (errorCause != null) {
+ disconnectReason = errorCause;
+ connectionState = ConnectionState.DETACHED;
+ connection.close();
+ }
+ }
+ }
+
+ private void attachImpl(DebugEventListener listener) throws IOException,
+ UnsupportedVersionException {
+ connectionState = ConnectionState.CONNECTING;
+
+ NetListener netListener = new NetListener() {
+ public void connectionClosed() {
+ }
+
+ public void eosReceived() {
+ debugSession.getV8CommandProcessor().processEos();
+ onDebuggerDetachedImpl(null);
+ }
+
+ public void messageReceived(Message message) {
+ JSONObject json;
+ try {
+ json = JsonUtil.jsonObjectFromJson(message.getContent());
+ } catch (ParseException e) {
+ LOGGER.log(Level.SEVERE, "Invalid JSON received: {0}", message.getContent());
+ return;
+ }
+ debugSession.getV8CommandProcessor().processIncomingJson(json);
+ }
+ };
+ connection.setNetListener(netListener);
+
+ connection.start();
+
+ connectionState = ConnectionState.EXPECTING_HANDSHAKE;
+
+ Handshaker.StandaloneV8.RemoteInfo remoteInfo;
+ try {
+ remoteInfo = handshaker.getRemoteInfo().get(WAIT_FOR_HANDSHAKE_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new IOException("Failed to get version", e);
+ } catch (TimeoutException e) {
+ throw new IOException("Timed out waiting for version", e);
+ }
+
+ String versionString = remoteInfo.getProtocolVersion();
+ // TODO(peter.rybin): check version here
+ if (versionString == null) {
+ throw new UnsupportedVersionException(null, null);
+ }
+
+ StandaloneVmImpl.this.savedRemoteInfo = remoteInfo;
+
+ StandaloneVmImpl.this.debugEventListener = listener;
+
+ debugSession.startCommunication();
+
+ connectionState = ConnectionState.CONNECTED;
+ }
+
+ public boolean detach() {
+ boolean res = onDebuggerDetachedImpl(null);
+ if (!res) {
+ return false;
+ }
+ connection.close();
+ return true;
+ }
+
+ public boolean isAttached() {
+ return connectionState == ConnectionState.CONNECTED;
+ }
+
+ private boolean onDebuggerDetachedImpl(Exception cause) {
+ synchronized (disconnectMonitor) {
+ if (!isAttached()) {
+ // We've already been notified.
+ return false;
+ }
+ connectionState = ConnectionState.DETACHED;
+ disconnectReason = cause;
+ }
+ if (debugEventListener != null) {
+ debugEventListener.disconnected();
+ }
+ return true;
+ }
+
+ @Override
+ protected DebugSession getDebugSession() {
+ return debugSession;
+ }
+
+ /**
+ * @return name of embedding application as it wished to name itself; might be null
+ */
+ public String getEmbedderName() {
+ return savedRemoteInfo.getEmbeddingHostName();
+ }
+
+ /**
+ * @return version of V8 implementation, format is unspecified; not null
+ */
+ public String getVmVersion() {
+ return savedRemoteInfo.getV8VmVersion();
+ }
+
+ public String getDisconnectReason() {
+ // Save volatile field in local variable.
+ Exception cause = disconnectReason;
+ if (cause == null) {
+ return null;
+ }
+ return cause.getMessage();
+ }
+
+ private final DebugSessionManager sessionManager = new DebugSessionManager() {
+ public DebugEventListener getDebugEventListener() {
+ return debugEventListener;
+ }
+
+ public void onDebuggerDetached() {
+ // Never called for standalone.
+ }
+ };
+
+ private final static Handshaker.StandaloneV8.RemoteInfo NULL_REMOTE_INFO =
+ new Handshaker.StandaloneV8.RemoteInfo() {
+ public String getEmbeddingHostName() {
+ return null;
+ }
+ public String getProtocolVersion() {
+ return null;
+ }
+ public String getV8VmVersion() {
+ return null;
+ }
+ };
+
+ private enum ConnectionState {
+ INIT,
+ CONNECTING,
+ EXPECTING_HANDSHAKE,
+ CONNECTED,
+ DETACHED
+ }
+
+ private static class V8CommandOutputImpl implements V8CommandOutput {
+ private final Connection outputConnection;
+
+ V8CommandOutputImpl(Connection outputConnection) {
+ this.outputConnection = outputConnection;
+ }
+ public void send(DebuggerMessage debuggerMessage, boolean immediate) {
+ String jsonString = JsonUtil.streamAwareToJson(debuggerMessage);
+ Message message = new Message(Collections.<String, String>emptyMap(), jsonString);
+
+ outputConnection.send(message);
+ // TODO(peter.rybin): support {@code immediate} in protocol
+ }
+ }
+}