Skip to content

Commit b1be96a

Browse files
authored
Add Hot Code Replace Handler (microsoft#144)
* Add Hot Code Replace Handler * Send class name list when classes redefined
1 parent b296bb2 commit b1be96a

8 files changed

Lines changed: 140 additions & 17 deletions

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.microsoft.java.debug.core.adapter.handler.ConfigurationDoneRequestHandler;
2525
import com.microsoft.java.debug.core.adapter.handler.DisconnectRequestHandler;
2626
import com.microsoft.java.debug.core.adapter.handler.EvaluateRequestHandler;
27+
import com.microsoft.java.debug.core.adapter.handler.HotCodeReplaceHandler;
2728
import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler;
2829
import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler;
2930
import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler;
@@ -104,6 +105,7 @@ private void initialize() {
104105
registerHandler(new VariablesRequestHandler());
105106
registerHandler(new SetVariableRequestHandler());
106107
registerHandler(new EvaluateRequestHandler());
108+
registerHandler(new HotCodeReplaceHandler());
107109
}
108110

109111
private void registerHandler(IDebugRequestHandler handler) {
@@ -113,6 +115,7 @@ private void registerHandler(IDebugRequestHandler handler) {
113115
handlerList = new ArrayList<>();
114116
requestHandlers.put(command, handlerList);
115117
}
118+
handler.initialize(debugContext);
116119
handlerList.add(handler);
117120
}
118121
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,27 @@ public int getValue() {
3939

4040
private String message;
4141

42+
private Object data;
43+
4244
public HotCodeReplaceEvent(EventType eventType, String message) {
4345
this.eventType = eventType;
4446
this.message = message;
4547
}
4648

49+
public HotCodeReplaceEvent(EventType eventType, String message, Object data) {
50+
this(eventType, message);
51+
this.data = data;
52+
}
53+
4754
public EventType getEventType() {
4855
return eventType;
4956
}
5057

5158
public String getMessage() {
5259
return message;
5360
}
61+
62+
public Object getData() {
63+
return data;
64+
}
5465
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
public interface IDebugRequestHandler {
2323
List<Requests.Command> getTargetCommands();
2424

25+
default void initialize(IDebugAdapterContext context) {
26+
}
27+
2528
CompletableFuture<Response> handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context);
2629

2730
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2017 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter.handler;
13+
14+
import java.util.Arrays;
15+
import java.util.List;
16+
import java.util.concurrent.CompletableFuture;
17+
18+
import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent;
19+
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
20+
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
21+
import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider;
22+
import com.microsoft.java.debug.core.protocol.Events;
23+
import com.microsoft.java.debug.core.protocol.Events.HotCodeReplaceEvent.ChangeType;
24+
import com.microsoft.java.debug.core.protocol.Messages.Response;
25+
import com.microsoft.java.debug.core.protocol.Requests;
26+
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
27+
import com.microsoft.java.debug.core.protocol.Requests.Command;
28+
29+
public class HotCodeReplaceHandler implements IDebugRequestHandler {
30+
@Override
31+
public List<Command> getTargetCommands() {
32+
return Arrays.asList(Requests.Command.REDEFINECLASSES);
33+
}
34+
35+
@Override
36+
public void initialize(IDebugAdapterContext context) {
37+
IDebugRequestHandler.super.initialize(context);
38+
IHotCodeReplaceProvider provider = context.getProvider(IHotCodeReplaceProvider.class);
39+
provider.getEventHub()
40+
.subscribe(event -> {
41+
if (event.getEventType() == HotCodeReplaceEvent.EventType.BUILD_COMPLETE) {
42+
context.getProtocolServer().sendEvent(new Events.HotCodeReplaceEvent(ChangeType.BUILD_COMPLETE, event.getMessage()));
43+
} else if (event.getEventType() == HotCodeReplaceEvent.EventType.STARTING) {
44+
context.getProtocolServer().sendEvent(new Events.HotCodeReplaceEvent(ChangeType.STARTING, event.getMessage()));
45+
} else if (event.getEventType() == HotCodeReplaceEvent.EventType.END) {
46+
context.getProtocolServer().sendEvent(new Events.HotCodeReplaceEvent(ChangeType.END, event.getMessage()));
47+
} else if (event.getEventType() == HotCodeReplaceEvent.EventType.ERROR) {
48+
context.getProtocolServer().sendEvent(new Events.HotCodeReplaceEvent(ChangeType.ERROR, event.getMessage()));
49+
} else if (event.getEventType() == HotCodeReplaceEvent.EventType.WARNING) {
50+
context.getProtocolServer().sendEvent(new Events.HotCodeReplaceEvent(ChangeType.WARNING, event.getMessage()));
51+
}
52+
});
53+
}
54+
55+
@Override
56+
public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response,
57+
IDebugAdapterContext context) {
58+
59+
IHotCodeReplaceProvider provider = context.getProvider(IHotCodeReplaceProvider.class);
60+
61+
return provider.redefineClasses().thenApply(classNames -> response);
62+
}
63+
64+
}

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.microsoft.java.debug.core.adapter.AdapterUtils;
2828
import com.microsoft.java.debug.core.adapter.BreakpointManager;
2929
import com.microsoft.java.debug.core.adapter.ErrorCode;
30+
import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent.EventType;
3031
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
3132
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
3233
import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider;
@@ -45,25 +46,29 @@ public class SetBreakpointsRequestHandler implements IDebugRequestHandler {
4546

4647
private BreakpointManager manager = new BreakpointManager();
4748

48-
private boolean isHcrInitialized = false;
49-
5049
@Override
5150
public List<Command> getTargetCommands() {
5251
return Arrays.asList(Command.SETBREAKPOINTS);
5352
}
5453

5554
@Override
56-
public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) {
57-
// TODO: This part logic should be part of post launching handler.
58-
// Add post launch logic and move the reinstall breakpoints logic there.
59-
if (!isHcrInitialized) {
60-
IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class);
61-
hcrProvider.onClassRedefined((typenames) -> {
62-
this.reinstallBreakpoints(context, typenames);
55+
public void initialize(IDebugAdapterContext context) {
56+
IDebugRequestHandler.super.initialize(context);
57+
IHotCodeReplaceProvider provider = context.getProvider(IHotCodeReplaceProvider.class);
58+
provider.getEventHub()
59+
.filter(event -> event.getEventType() == EventType.END)
60+
.subscribe(event -> {
61+
try {
62+
List<String> classNames = (List<String>) event.getData();
63+
reinstallBreakpoints(context, classNames);
64+
} catch (Exception e) {
65+
logger.severe(e.toString());
66+
}
6367
});
64-
isHcrInitialized = true;
65-
}
68+
}
6669

70+
@Override
71+
public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) {
6772
if (context.getDebugSession() == null) {
6873
return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Empty debug session.");
6974
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,22 @@ public BreakpointEvent(String reason, Types.Breakpoint breakpoint) {
176176
this.breakpoint = breakpoint;
177177
}
178178
}
179+
180+
public static class HotCodeReplaceEvent extends DebugEvent {
181+
public enum ChangeType {
182+
ERROR, WARNING, STARTING, END, BUILD_COMPLETE
183+
}
184+
185+
public ChangeType changeType;
186+
public String message;
187+
188+
/**
189+
* Constructor.
190+
*/
191+
public HotCodeReplaceEvent(ChangeType changeType, String message) {
192+
super("hotcodereplace");
193+
this.changeType = changeType;
194+
this.message = message;
195+
}
196+
}
179197
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ public static class EvaluateArguments extends Arguments {
255255
public ValueFormat format;
256256
}
257257

258+
public static class RedefineClassesArguments extends Arguments {
259+
260+
}
261+
258262
public static enum Command {
259263
INITIALIZE("initialize", InitializeArguments.class),
260264
LAUNCH("launch", LaunchArguments.class),
@@ -277,6 +281,7 @@ public static enum Command {
277281
SETFUNCTIONBREAKPOINTS("setFunctionBreakpoints", SetFunctionBreakpointsArguments.class),
278282
EVALUATE("evaluate", EvaluateArguments.class),
279283
RUNINTERMINAL("runInTerminal", RunInTerminalRequestArguments.class),
284+
REDEFINECLASSES("redefineClasses", RedefineClassesArguments.class),
280285
UNSUPPORTED("", Arguments.class);
281286

282287
private String command;

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ public class JavaHotCodeReplaceProvider implements IHotCodeReplaceProvider, IRes
9999

100100
private PublishSubject<HotCodeReplaceEvent> eventSubject = PublishSubject.<HotCodeReplaceEvent>create();
101101

102+
private List<IResource> deltaResources = new ArrayList();
103+
104+
private List<String> deltaClassNames = new ArrayList();
105+
102106
/**
103107
* Visitor for resource deltas.
104108
*/
@@ -274,10 +278,12 @@ public void resourceChanged(IResourceChangeEvent event) {
274278
ChangedClassFilesVisitor visitor = getChangedClassFiles(event);
275279
if (visitor != null) {
276280
List<IResource> resources = visitor.getChangedClassFiles();
277-
List<String> fullyQualifiedName = visitor.getQualifiedNamesList();
278-
if (resources != null && !resources.isEmpty()) {
279-
doHotCodeReplace(resources, fullyQualifiedName);
281+
List<String> classNames = visitor.getQualifiedNamesList();
282+
synchronized (this) {
283+
deltaResources.addAll(resources);
284+
deltaClassNames.addAll(classNames);
280285
}
286+
publishEvent(HotCodeReplaceEvent.EventType.BUILD_COMPLETE, "Build completed.");
281287
}
282288
}
283289
}
@@ -291,8 +297,12 @@ public void onClassRedefined(Consumer<List<String>> consumer) {
291297
public CompletableFuture<List<String>> redefineClasses() {
292298
return CompletableFuture.supplyAsync(() -> {
293299
List<String> classNames = new ArrayList();
294-
295-
// TODO: fill the class name collection by calling doHotCodeReplace
300+
synchronized (this) {
301+
classNames.addAll(deltaClassNames);
302+
doHotCodeReplace(deltaResources, deltaClassNames);
303+
deltaResources.clear();
304+
deltaClassNames.clear();
305+
}
296306
return classNames;
297307
});
298308
}
@@ -306,6 +316,10 @@ private void publishEvent(HotCodeReplaceEvent.EventType type, String message) {
306316
eventSubject.onNext(new HotCodeReplaceEvent(type, message));
307317
}
308318

319+
private void publishEvent(HotCodeReplaceEvent.EventType type, String message, Object data) {
320+
eventSubject.onNext(new HotCodeReplaceEvent(type, message, data));
321+
}
322+
309323
private void doHotCodeReplace(List<IResource> resourcesToReplace, List<String> qualifiedNamesToReplace) {
310324
if (context == null || currentDebugSession == null) {
311325
return;
@@ -358,7 +372,7 @@ private void doHotCodeReplace(List<IResource> resourcesToReplace, List<String> q
358372
} catch (DebugException e) {
359373
logger.log(Level.SEVERE, "Failed to complete hot code replace: " + e.getMessage(), e);
360374
} finally {
361-
publishEvent(HotCodeReplaceEvent.EventType.END, "Completed hot code replace");
375+
publishEvent(HotCodeReplaceEvent.EventType.END, "Completed hot code replace", qualifiedNamesToReplace);
362376
}
363377

364378
threadFrameMap.clear();

0 commit comments

Comments
 (0)