Skip to content

Commit 8a96ff7

Browse files
Support send request in protocol server (microsoft#121)
* Support send request in protocol server Signed-off-by: Jinbo Wang <jinbwan@microsoft.com> * Make sure the call back only be execute once * return CompletableFuture for sending request
1 parent e6fae9c commit 8a96ff7

File tree

13 files changed

+222
-71
lines changed

13 files changed

+222
-71
lines changed

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.util.List;
1717
import java.util.Map;
1818
import java.util.concurrent.CompletableFuture;
19-
import java.util.function.Consumer;
2019
import java.util.logging.Level;
2120
import java.util.logging.Logger;
2221

@@ -35,6 +34,7 @@
3534
import com.microsoft.java.debug.core.adapter.handler.StackTraceRequestHandler;
3635
import com.microsoft.java.debug.core.adapter.handler.ThreadsRequestHandler;
3736
import com.microsoft.java.debug.core.adapter.handler.VariablesRequestHandler;
37+
import com.microsoft.java.debug.core.protocol.IProtocolServer;
3838
import com.microsoft.java.debug.core.protocol.JsonUtils;
3939
import com.microsoft.java.debug.core.protocol.Messages;
4040
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
@@ -49,8 +49,8 @@ public class DebugAdapter implements IDebugAdapter {
4949
/**
5050
* Constructor.
5151
*/
52-
public DebugAdapter(Consumer<Messages.ProtocolMessage> messageConsumer, IProviderContext providerContext) {
53-
debugContext = new DebugAdapterContext(messageConsumer, providerContext);
52+
public DebugAdapter(IProtocolServer server, IProviderContext providerContext) {
53+
this.debugContext = new DebugAdapterContext(server, providerContext);
5454
requestHandlers = new HashMap<>();
5555
initialize();
5656
}
@@ -87,6 +87,7 @@ public CompletableFuture<Messages.Response> dispatchRequest(Messages.Request req
8787

8888
private void initialize() {
8989
// Register request handlers.
90+
// When there are multiple handlers registered for the same request, follow the rule "first register, first execute".
9091
registerHandler(new InitializeRequestHandler());
9192
registerHandler(new LaunchRequestHandler());
9293
registerHandler(new AttachRequestHandler());
@@ -113,4 +114,4 @@ private void registerHandler(IDebugRequestHandler handler) {
113114
handlerList.add(handler);
114115
}
115116
}
116-
}
117+
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,17 @@
1414
import java.nio.charset.Charset;
1515
import java.util.Collections;
1616
import java.util.Map;
17-
import java.util.function.Consumer;
1817

1918
import com.microsoft.java.debug.core.IDebugSession;
2019
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
2120
import com.microsoft.java.debug.core.adapter.variables.VariableFormatterFactory;
22-
import com.microsoft.java.debug.core.protocol.Events.DebugEvent;
23-
import com.microsoft.java.debug.core.protocol.Messages;
21+
import com.microsoft.java.debug.core.protocol.IProtocolServer;
2422

2523
public class DebugAdapterContext implements IDebugAdapterContext {
2624
private static final int MAX_CACHE_ITEMS = 10000;
2725
private Map<String, String> sourceMappingCache = Collections.synchronizedMap(new LRUCache<>(MAX_CACHE_ITEMS));
2826
private IProviderContext providerContext;
29-
private Consumer<Messages.ProtocolMessage> messageConsumer;
27+
private IProtocolServer server;
3028

3129
private IDebugSession debugSession;
3230
private boolean debuggerLinesStartAt1 = true;
@@ -44,14 +42,14 @@ public class DebugAdapterContext implements IDebugAdapterContext {
4442
private RecyclableObjectPool<Long, Object> recyclableIdPool = new RecyclableObjectPool<>();
4543
private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter();
4644

47-
public DebugAdapterContext(Consumer<Messages.ProtocolMessage> messageConsumer, IProviderContext providerContext) {
45+
public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) {
4846
this.providerContext = providerContext;
49-
this.messageConsumer = messageConsumer;
47+
this.server = server;
5048
}
5149

5250
@Override
53-
public void sendEvent(DebugEvent event) {
54-
messageConsumer.accept(new Messages.Event(event.type, event));
51+
public IProtocolServer getProtocolServer() {
52+
return server;
5553
}
5654

5755
@Override

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,10 @@
1616

1717
import com.microsoft.java.debug.core.IDebugSession;
1818
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
19-
import com.microsoft.java.debug.core.protocol.Events;
19+
import com.microsoft.java.debug.core.protocol.IProtocolServer;
2020

2121
public interface IDebugAdapterContext {
22-
/**
23-
* Send debug event to the DA.
24-
*
25-
* @param event
26-
* the debug event
27-
*/
28-
void sendEvent(Events.DebugEvent event);
22+
IProtocolServer getProtocolServer();
2923

3024
<T extends IProvider> T getProvider(Class<T> clazz);
3125

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.io.InputStream;
1515
import java.io.OutputStream;
16+
import java.util.concurrent.CompletableFuture;
1617
import java.util.logging.Level;
1718
import java.util.logging.Logger;
1819

@@ -38,14 +39,13 @@ public class ProtocolServer extends AbstractProtocolServer {
3839
*/
3940
public ProtocolServer(InputStream input, OutputStream output, IProviderContext context) {
4041
super(input, output);
41-
this.debugAdapter = new DebugAdapter((message) -> {
42-
sendMessage(message);
43-
}, context);
42+
this.debugAdapter = new DebugAdapter(this, context);
4443
}
4544

4645
/**
4746
* A while-loop to parse input data and send output data constantly.
4847
*/
48+
@Override
4949
public void run() {
5050
usageDataSession.reportStart();
5151
super.run();
@@ -54,28 +54,37 @@ public void run() {
5454
}
5555

5656
@Override
57-
protected void sendMessage(Messages.ProtocolMessage message) {
58-
super.sendMessage(message);
59-
if (message instanceof Messages.Response) {
60-
usageDataSession.recordResponse((Messages.Response) message);
61-
} else if (message instanceof Messages.Request) {
62-
usageDataSession.recordRequest((Messages.Request) message);
63-
}
57+
public void sendResponse(Messages.Response response) {
58+
usageDataSession.recordResponse(response);
59+
super.sendResponse(response);
60+
}
61+
62+
@Override
63+
public CompletableFuture<Messages.Response> sendRequest(Messages.Request request) {
64+
usageDataSession.recordRequest(request);
65+
return super.sendRequest(request);
66+
}
67+
68+
@Override
69+
public CompletableFuture<Messages.Response> sendRequest(Messages.Request request, long timeout) {
70+
usageDataSession.recordRequest(request);
71+
return super.sendRequest(request, timeout);
6472
}
6573

74+
@Override
6675
protected void dispatchRequest(Messages.Request request) {
6776
usageDataSession.recordRequest(request);
6877
this.debugAdapter.dispatchRequest(request).whenComplete((response, ex) -> {
6978
if (response != null) {
70-
sendMessage(response);
79+
sendResponse(response);
7180
} else if (ex != null) {
7281
logger.log(Level.SEVERE, String.format("DebugSession dispatch exception: %s", ex.toString()), ex);
73-
sendMessage(AdapterUtils.setErrorResponse(response,
82+
sendResponse(AdapterUtils.setErrorResponse(response,
7483
ErrorCode.UNKNOWN_FAILURE,
7584
ex.getMessage() != null ? ex.getMessage() : ex.toString()));
7685
} else {
7786
logger.log(Level.SEVERE, "The request dispatcher should not return null response.");
78-
sendMessage(AdapterUtils.setErrorResponse(response,
87+
sendResponse(AdapterUtils.setErrorResponse(response,
7988
ErrorCode.UNKNOWN_FAILURE,
8089
"The request dispatcher should not return null response."));
8190
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
7171
+ "Debugger JVM version: %s\n"
7272
+ "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion);
7373
logger.warning(warnMessage);
74-
context.sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage));
74+
context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage));
7575
}
7676
}
7777
} catch (IOException | IllegalConnectorArgumentsException e) {
@@ -89,7 +89,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
8989

9090
// Send an InitializedEvent to indicate that the debugger is ready to accept configuration requests
9191
// (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest).
92-
context.sendEvent(new Events.InitializedEvent());
92+
context.getProtocolServer().sendEvent(new Events.InitializedEvent());
9393
return CompletableFuture.completedFuture(response);
9494
}
9595

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
5757
debugSession.start();
5858
return CompletableFuture.completedFuture(response);
5959
} else {
60-
context.sendEvent(new Events.TerminatedEvent());
60+
context.getProtocolServer().sendEvent(new Events.TerminatedEvent());
6161
return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Failed to launch debug session, the debugger will exit.");
6262
}
6363
}
@@ -68,15 +68,15 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
6868
if (event instanceof VMStartEvent) {
6969
if (context.isVmStopOnEntry()) {
7070
DebugUtility.stopOnEntry(debugSession, context.getMainClass()).thenAccept(threadId -> {
71-
context.sendEvent(new Events.StoppedEvent("entry", threadId));
71+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("entry", threadId));
7272
});
7373
}
7474
} else if (event instanceof VMDeathEvent) {
7575
context.setVmTerminated();
76-
context.sendEvent(new Events.ExitedEvent(0));
76+
context.getProtocolServer().sendEvent(new Events.ExitedEvent(0));
7777
} else if (event instanceof VMDisconnectEvent) {
7878
context.setVmTerminated();
79-
context.sendEvent(new Events.TerminatedEvent());
79+
context.getProtocolServer().sendEvent(new Events.TerminatedEvent());
8080
// Terminate eventHub thread.
8181
try {
8282
debugSession.getEventHub().close();
@@ -86,27 +86,27 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
8686
} else if (event instanceof ThreadStartEvent) {
8787
ThreadReference startThread = ((ThreadStartEvent) event).thread();
8888
Events.ThreadEvent threadEvent = new Events.ThreadEvent("started", startThread.uniqueID());
89-
context.sendEvent(threadEvent);
89+
context.getProtocolServer().sendEvent(threadEvent);
9090
} else if (event instanceof ThreadDeathEvent) {
9191
ThreadReference deathThread = ((ThreadDeathEvent) event).thread();
9292
Events.ThreadEvent threadDeathEvent = new Events.ThreadEvent("exited", deathThread.uniqueID());
93-
context.sendEvent(threadDeathEvent);
93+
context.getProtocolServer().sendEvent(threadDeathEvent);
9494
} else if (event instanceof BreakpointEvent) {
9595
if (debugEvent.eventSet.size() > 1 && debugEvent.eventSet.stream().anyMatch(t -> t instanceof StepEvent)) {
9696
// The StepEvent and BreakpointEvent are grouped in the same event set only if they occurs at the same location and in the same thread.
9797
// In order to avoid two duplicated StoppedEvents, the debugger will skip the BreakpointEvent.
9898
} else {
9999
ThreadReference bpThread = ((BreakpointEvent) event).thread();
100-
context.sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
100+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
101101
debugEvent.shouldResume = false;
102102
}
103103
} else if (event instanceof StepEvent) {
104104
ThreadReference stepThread = ((StepEvent) event).thread();
105-
context.sendEvent(new Events.StoppedEvent("step", stepThread.uniqueID()));
105+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", stepThread.uniqueID()));
106106
debugEvent.shouldResume = false;
107107
} else if (event instanceof ExceptionEvent) {
108108
ThreadReference thread = ((ExceptionEvent) event).thread();
109-
context.sendEvent(new Events.StoppedEvent("exception", thread.uniqueID()));
109+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID()));
110110
debugEvent.shouldResume = false;
111111
} else {
112112
isImportantEvent = false;

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
142142
debuggeeConsole.onStdout((output) -> {
143143
// When DA receives a new OutputEvent, it just shows that on Debug Console and doesn't affect the DA's dispatching workflow.
144144
// That means the debugger can send OutputEvent to DA at any time.
145-
context.sendEvent(Events.OutputEvent.createStdoutOutput(output));
145+
context.getProtocolServer().sendEvent(Events.OutputEvent.createStdoutOutput(output));
146146
});
147147

148148
debuggeeConsole.onStderr((err) -> {
149-
context.sendEvent(Events.OutputEvent.createStderrOutput(err));
149+
context.getProtocolServer().sendEvent(Events.OutputEvent.createStderrOutput(err));
150150
});
151151
debuggeeConsole.start();
152152
} catch (IOException | IllegalConnectorArgumentsException | VMStartException e) {
@@ -164,7 +164,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
164164

165165
// Send an InitializedEvent to indicate that the debugger is ready to accept configuration requests
166166
// (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest).
167-
context.sendEvent(new Events.InitializedEvent());
167+
context.getProtocolServer().sendEvent(new Events.InitializedEvent());
168168
return CompletableFuture.completedFuture(response);
169169
}
170170

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
8888
if (toAdds[i] == added[i] && added[i].className() != null) {
8989
added[i].install().thenAccept(bp -> {
9090
Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("new", this.convertDebuggerBreakpointToClient(bp, context));
91-
context.sendEvent(bpEvent);
91+
context.getProtocolServer().sendEvent(bpEvent);
9292
});
9393
} else if (toAdds[i].hitCount() != added[i].hitCount() && added[i].className() != null) {
9494
// Update hitCount condition.

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ private CompletableFuture<Response> pause(Requests.PauseArguments arguments, Res
119119
if (thread != null) {
120120
try {
121121
thread.suspend();
122-
context.sendEvent(new Events.StoppedEvent("pause", arguments.threadId));
122+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId));
123123
} catch (VMDisconnectedException ex) {
124124
return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.VM_TERMINATED, "Target VM is already terminated.");
125125
}
126126
} else {
127127
context.getDebugSession().suspend();
128-
context.sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true));
128+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true));
129129
}
130130
return CompletableFuture.completedFuture(response);
131131
}

0 commit comments

Comments
 (0)