author | Eugene Ostroukhov <eugeneo@symbian.org> |
Thu, 18 Mar 2010 11:56:59 -0700 | |
changeset 276 | f2f4a1259de8 |
parent 56 | 22f918ed49f7 |
permissions | -rw-r--r-- |
2 | 1 |
// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 |
// Use of this source code is governed by a BSD-style license that can be |
|
3 |
// found in the LICENSE file. |
|
4 |
||
5 |
package org.chromium.sdk.internal; |
|
6 |
||
7 |
import java.io.IOException; |
|
8 |
import java.util.Collections; |
|
9 |
import java.util.concurrent.ExecutionException; |
|
10 |
import java.util.concurrent.TimeUnit; |
|
11 |
import java.util.concurrent.TimeoutException; |
|
12 |
import java.util.logging.Level; |
|
13 |
import java.util.logging.Logger; |
|
14 |
||
15 |
import org.chromium.sdk.DebugEventListener; |
|
16 |
import org.chromium.sdk.StandaloneVm; |
|
17 |
import org.chromium.sdk.UnsupportedVersionException; |
|
18 |
import org.chromium.sdk.internal.protocol.data.ContextHandle; |
|
19 |
import org.chromium.sdk.internal.tools.v8.V8CommandOutput; |
|
20 |
import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage; |
|
21 |
import org.chromium.sdk.internal.transport.Connection; |
|
22 |
import org.chromium.sdk.internal.transport.Handshaker; |
|
23 |
import org.chromium.sdk.internal.transport.Message; |
|
24 |
import org.chromium.sdk.internal.transport.SocketConnection; |
|
25 |
import org.chromium.sdk.internal.transport.Connection.NetListener; |
|
26 |
import org.json.simple.JSONObject; |
|
27 |
import org.json.simple.parser.ParseException; |
|
28 |
||
29 |
/** |
|
30 |
* Implementation of {@code StandaloneVm}. Currently knows nothing about |
|
31 |
* contexts, so all existing V8 contexts are presented mixed together. |
|
32 |
*/ |
|
33 |
class StandaloneVmImpl extends JavascriptVmImpl implements StandaloneVm { |
|
34 |
||
35 |
/** The class logger. */ |
|
36 |
private static final Logger LOGGER = |
|
37 |
Logger.getLogger(StandaloneVmImpl.class.getName()); |
|
38 |
||
39 |
private static final int WAIT_FOR_HANDSHAKE_TIMEOUT_MS = 3000; |
|
40 |
||
41 |
private static final V8ContextFilter CONTEXT_FILTER = new V8ContextFilter() { |
|
42 |
public boolean isContextOurs(ContextHandle contextHandle) { |
|
43 |
// We do not check context in standalone V8 mode. |
|
44 |
return true; |
|
45 |
} |
|
46 |
}; |
|
47 |
||
48 |
private final SocketConnection connection; |
|
49 |
private final Handshaker.StandaloneV8 handshaker; |
|
50 |
||
51 |
private final DebugSession debugSession; |
|
52 |
||
53 |
private DebugEventListener debugEventListener = null; |
|
54 |
private volatile ConnectionState connectionState = ConnectionState.INIT; |
|
55 |
||
56 |
private volatile Exception disconnectReason = null; |
|
57 |
private volatile Handshaker.StandaloneV8.RemoteInfo savedRemoteInfo = NULL_REMOTE_INFO; |
|
58 |
||
59 |
private final Object disconnectMonitor = new Object(); |
|
60 |
||
61 |
StandaloneVmImpl(SocketConnection connection, Handshaker.StandaloneV8 handshaker) { |
|
62 |
this.connection = connection; |
|
63 |
this.handshaker = handshaker; |
|
64 |
V8CommandOutputImpl v8CommandOutput = new V8CommandOutputImpl(connection); |
|
65 |
this.debugSession = new DebugSession(sessionManager, CONTEXT_FILTER, v8CommandOutput); |
|
66 |
} |
|
67 |
||
68 |
public void attach(DebugEventListener listener) throws IOException, UnsupportedVersionException { |
|
69 |
Exception errorCause = null; |
|
70 |
try { |
|
71 |
attachImpl(listener); |
|
72 |
} catch (IOException e) { |
|
73 |
errorCause = e; |
|
74 |
throw e; |
|
75 |
} catch (UnsupportedVersionException e) { |
|
76 |
errorCause = e; |
|
77 |
throw e; |
|
78 |
} finally { |
|
79 |
if (errorCause != null) { |
|
80 |
disconnectReason = errorCause; |
|
81 |
connectionState = ConnectionState.DETACHED; |
|
82 |
connection.close(); |
|
83 |
} |
|
84 |
} |
|
85 |
} |
|
86 |
||
87 |
private void attachImpl(DebugEventListener listener) throws IOException, |
|
88 |
UnsupportedVersionException { |
|
89 |
connectionState = ConnectionState.CONNECTING; |
|
90 |
||
91 |
NetListener netListener = new NetListener() { |
|
92 |
public void connectionClosed() { |
|
93 |
} |
|
94 |
||
95 |
public void eosReceived() { |
|
96 |
debugSession.getV8CommandProcessor().processEos(); |
|
97 |
onDebuggerDetachedImpl(null); |
|
98 |
} |
|
99 |
||
100 |
public void messageReceived(Message message) { |
|
101 |
JSONObject json; |
|
102 |
try { |
|
103 |
json = JsonUtil.jsonObjectFromJson(message.getContent()); |
|
104 |
} catch (ParseException e) { |
|
105 |
LOGGER.log(Level.SEVERE, "Invalid JSON received: {0}", message.getContent()); |
|
106 |
return; |
|
107 |
} |
|
108 |
debugSession.getV8CommandProcessor().processIncomingJson(json); |
|
109 |
} |
|
110 |
}; |
|
111 |
connection.setNetListener(netListener); |
|
112 |
||
113 |
connection.start(); |
|
114 |
||
115 |
connectionState = ConnectionState.EXPECTING_HANDSHAKE; |
|
116 |
||
117 |
Handshaker.StandaloneV8.RemoteInfo remoteInfo; |
|
118 |
try { |
|
119 |
remoteInfo = handshaker.getRemoteInfo().get(WAIT_FOR_HANDSHAKE_TIMEOUT_MS, |
|
120 |
TimeUnit.MILLISECONDS); |
|
121 |
} catch (InterruptedException e) { |
|
122 |
throw new RuntimeException(e); |
|
123 |
} catch (ExecutionException e) { |
|
276
f2f4a1259de8
Bug 2065 - Pull updated Chrome Developer Tools into the workspace
Eugene Ostroukhov <eugeneo@symbian.org>
parents:
56
diff
changeset
|
124 |
throw newIOException("Failed to get version", e); |
2 | 125 |
} catch (TimeoutException e) { |
276
f2f4a1259de8
Bug 2065 - Pull updated Chrome Developer Tools into the workspace
Eugene Ostroukhov <eugeneo@symbian.org>
parents:
56
diff
changeset
|
126 |
throw newIOException("Timed out waiting for version", e); |
2 | 127 |
} |
128 |
||
129 |
String versionString = remoteInfo.getProtocolVersion(); |
|
130 |
// TODO(peter.rybin): check version here |
|
131 |
if (versionString == null) { |
|
132 |
throw new UnsupportedVersionException(null, null); |
|
133 |
} |
|
134 |
||
135 |
StandaloneVmImpl.this.savedRemoteInfo = remoteInfo; |
|
136 |
||
137 |
StandaloneVmImpl.this.debugEventListener = listener; |
|
138 |
||
139 |
debugSession.startCommunication(); |
|
140 |
||
141 |
connectionState = ConnectionState.CONNECTED; |
|
142 |
} |
|
143 |
||
144 |
public boolean detach() { |
|
145 |
boolean res = onDebuggerDetachedImpl(null); |
|
146 |
if (!res) { |
|
147 |
return false; |
|
148 |
} |
|
149 |
connection.close(); |
|
150 |
return true; |
|
151 |
} |
|
152 |
||
153 |
public boolean isAttached() { |
|
154 |
return connectionState == ConnectionState.CONNECTED; |
|
155 |
} |
|
156 |
||
157 |
private boolean onDebuggerDetachedImpl(Exception cause) { |
|
158 |
synchronized (disconnectMonitor) { |
|
159 |
if (!isAttached()) { |
|
160 |
// We've already been notified. |
|
161 |
return false; |
|
162 |
} |
|
163 |
connectionState = ConnectionState.DETACHED; |
|
164 |
disconnectReason = cause; |
|
165 |
} |
|
166 |
if (debugEventListener != null) { |
|
167 |
debugEventListener.disconnected(); |
|
168 |
} |
|
169 |
return true; |
|
170 |
} |
|
171 |
||
172 |
@Override |
|
173 |
protected DebugSession getDebugSession() { |
|
174 |
return debugSession; |
|
175 |
} |
|
176 |
||
177 |
/** |
|
178 |
* @return name of embedding application as it wished to name itself; might be null |
|
179 |
*/ |
|
180 |
public String getEmbedderName() { |
|
181 |
return savedRemoteInfo.getEmbeddingHostName(); |
|
182 |
} |
|
183 |
||
184 |
/** |
|
185 |
* @return version of V8 implementation, format is unspecified; not null |
|
186 |
*/ |
|
187 |
public String getVmVersion() { |
|
188 |
return savedRemoteInfo.getV8VmVersion(); |
|
189 |
} |
|
190 |
||
191 |
public String getDisconnectReason() { |
|
192 |
// Save volatile field in local variable. |
|
193 |
Exception cause = disconnectReason; |
|
194 |
if (cause == null) { |
|
195 |
return null; |
|
196 |
} |
|
197 |
return cause.getMessage(); |
|
198 |
} |
|
199 |
||
200 |
private final DebugSessionManager sessionManager = new DebugSessionManager() { |
|
201 |
public DebugEventListener getDebugEventListener() { |
|
202 |
return debugEventListener; |
|
203 |
} |
|
204 |
||
205 |
public void onDebuggerDetached() { |
|
206 |
// Never called for standalone. |
|
207 |
} |
|
208 |
}; |
|
209 |
||
210 |
private final static Handshaker.StandaloneV8.RemoteInfo NULL_REMOTE_INFO = |
|
211 |
new Handshaker.StandaloneV8.RemoteInfo() { |
|
212 |
public String getEmbeddingHostName() { |
|
213 |
return null; |
|
214 |
} |
|
215 |
public String getProtocolVersion() { |
|
216 |
return null; |
|
217 |
} |
|
218 |
public String getV8VmVersion() { |
|
219 |
return null; |
|
220 |
} |
|
221 |
}; |
|
222 |
||
223 |
private enum ConnectionState { |
|
224 |
INIT, |
|
225 |
CONNECTING, |
|
226 |
EXPECTING_HANDSHAKE, |
|
227 |
CONNECTED, |
|
228 |
DETACHED |
|
229 |
} |
|
230 |
||
231 |
private static class V8CommandOutputImpl implements V8CommandOutput { |
|
232 |
private final Connection outputConnection; |
|
233 |
||
234 |
V8CommandOutputImpl(Connection outputConnection) { |
|
235 |
this.outputConnection = outputConnection; |
|
236 |
} |
|
237 |
public void send(DebuggerMessage debuggerMessage, boolean immediate) { |
|
238 |
String jsonString = JsonUtil.streamAwareToJson(debuggerMessage); |
|
239 |
Message message = new Message(Collections.<String, String>emptyMap(), jsonString); |
|
240 |
||
241 |
outputConnection.send(message); |
|
242 |
// TODO(peter.rybin): support {@code immediate} in protocol |
|
243 |
} |
|
244 |
} |
|
245 |
} |