Skip to content

Commit 51e5437

Browse files
fix: progress manager and tests (#222)
1 parent 4a658cd commit 51e5437

6 files changed

Lines changed: 237 additions & 406 deletions

File tree

plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/StopScanHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public StopScanHandler() {
2424

2525
@Override
2626
public Object execute(ExecutionEvent event) throws ExecutionException {
27-
SnykExtendedLanguageClient.getInstance().cancelAllProgresses();
27+
SnykExtendedLanguageClient.getInstance().getProgressManager().cancelAll();
2828
ScanState.getInstance().clearAllScanStates();
2929
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
3030
IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
Lines changed: 29 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,36 @@
11
package io.snyk.languageserver.protocolextension;
22

3-
import io.snyk.eclipse.plugin.utils.SnykLogger;
4-
import org.apache.commons.lang3.tuple.ImmutablePair;
5-
import org.apache.commons.lang3.tuple.Pair;
6-
import org.eclipse.core.runtime.IProgressMonitor;
7-
import org.eclipse.core.runtime.IStatus;
8-
import org.eclipse.core.runtime.Status;
9-
import org.eclipse.core.runtime.jobs.Job;
10-
import org.eclipse.lsp4j.ProgressParams;
11-
import org.eclipse.lsp4j.WorkDoneProgressBegin;
12-
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
13-
import org.eclipse.lsp4j.WorkDoneProgressEnd;
14-
import org.eclipse.lsp4j.WorkDoneProgressNotification;
15-
import org.eclipse.lsp4j.WorkDoneProgressReport;
16-
import org.eclipse.lsp4j.jsonrpc.messages.Either;
3+
import static java.util.Collections.synchronizedSet;
174

18-
import java.util.concurrent.CompletableFuture;
19-
import java.util.concurrent.ConcurrentHashMap;
5+
import java.util.HashSet;
6+
import java.util.Set;
207

21-
@SuppressWarnings("BusyWait")
228
public class ProgressManager {
23-
final ConcurrentHashMap<String, Pair<SnykBackgroundJob, IProgressMonitor>> progresses = new ConcurrentHashMap<>();
24-
final ConcurrentHashMap<IProgressMonitor, Integer> currentPercentage = new ConcurrentHashMap<>();
25-
SnykBackgroundJobBuilder jobBuilder = new SnykBackgroundJobBuilder();
26-
27-
ProgressManager(SnykBackgroundJobBuilder jobBuilder) {
28-
this.jobBuilder = jobBuilder;
29-
}
30-
31-
ProgressManager() {
32-
}
33-
34-
class SnykBackgroundJobBuilder {
35-
SnykBackgroundJob build(WorkDoneProgressCreateParams param) {
36-
return new SnykBackgroundJob(param);
37-
}
38-
}
39-
40-
class SnykBackgroundJob extends Job {
41-
private final WorkDoneProgressCreateParams param;
42-
43-
public SnykBackgroundJob(WorkDoneProgressCreateParams param) {
44-
super("Snyk Background Job");
45-
this.param = param;
46-
}
47-
48-
protected IStatus run(IProgressMonitor monitor) {
49-
IStatus cancelStatus = doWork(progresses.get(getToken(param.getToken())).getKey(), monitor, param);
50-
if (cancelStatus != null)
51-
return cancelStatus;
52-
return Status.OK_STATUS;
53-
}
54-
}
55-
56-
public CompletableFuture<Void> createProgress(WorkDoneProgressCreateParams param) {
57-
synchronized (progresses) {
58-
var job = jobBuilder.build(param);
59-
progresses.put(getToken(param.getToken()), new ImmutablePair<>(job, null));
60-
job.schedule();
61-
return CompletableFuture.completedFuture(null);
62-
}
63-
}
64-
65-
IStatus doWork(SnykBackgroundJob job, IProgressMonitor monitor, WorkDoneProgressCreateParams param) {
66-
var token = getToken(param.getToken());
67-
progresses.put(token, new ImmutablePair<>(job, monitor));
68-
currentPercentage.put(monitor, 0);
69-
while (progresses.containsKey(token)) {
70-
try {
71-
if (monitor.isCanceled()) {
72-
return Status.CANCEL_STATUS;
73-
}
74-
Thread.sleep(100);
75-
} catch (InterruptedException e) {
76-
SnykLogger.logError(e);
77-
Thread.currentThread().interrupt();
78-
}
79-
}
80-
return null;
81-
}
82-
83-
String getToken(Either<String, Integer> param) {
84-
return param.getLeft();
85-
}
86-
87-
public void updateProgress(ProgressParams param) {
88-
String progressToken = getToken(param.getToken());
89-
while (!progresses.containsKey(progressToken) || progresses.get(progressToken).getValue() == null) {
90-
try {
91-
Thread.sleep(1000);
92-
} catch (InterruptedException e) {
93-
SnykLogger.logError(e);
94-
Thread.currentThread().interrupt();
95-
}
96-
}
97-
synchronized (progresses) {
98-
WorkDoneProgressNotification notification = param.getValue().getLeft();
99-
IProgressMonitor monitor = progresses.get(progressToken).getValue();
100-
switch (notification.getKind()) {
101-
case begin:
102-
begin((WorkDoneProgressBegin) notification, monitor);
103-
break;
104-
case report:
105-
report((WorkDoneProgressReport) notification, monitor);
106-
break;
107-
case end:
108-
end((WorkDoneProgressEnd) notification, monitor);
109-
progresses.remove(progressToken);
110-
currentPercentage.remove(monitor);
111-
break;
112-
}
113-
}
114-
}
115-
116-
private void end(WorkDoneProgressEnd end, IProgressMonitor monitor) {
117-
if (null == end || null == monitor)
118-
return;
119-
monitor.subTask(end.getMessage());
120-
monitor.done();
121-
}
122-
123-
private void report(WorkDoneProgressReport report, IProgressMonitor monitor) {
124-
if (report.getPercentage() == null) return;
125-
var worked = currentPercentage.get(monitor) != null
126-
? Math.min(currentPercentage.get(monitor), report.getPercentage())
127-
: 0;
128-
129-
if (null != report.getMessage() && !report.getMessage().isBlank()) {
130-
monitor.subTask(report.getMessage());
131-
}
132-
133-
monitor.worked(report.getPercentage() - worked);
134-
currentPercentage.put(monitor, report.getPercentage());
135-
}
136-
137-
private void begin(WorkDoneProgressBegin begin, IProgressMonitor monitor) {
138-
if (null == begin || null == monitor)
139-
return;
140-
if (begin.getPercentage() == null) {
141-
monitor.beginTask(begin.getTitle(), IProgressMonitor.UNKNOWN);
142-
} else {
143-
monitor.beginTask(begin.getTitle(), 100);
144-
}
145-
String message = begin.getMessage();
146-
if (message != null && !message.isBlank())
147-
monitor.subTask(message);
148-
}
9+
SnykExtendedLanguageClient lc = SnykExtendedLanguageClient.getInstance();
10+
Set<String> progresses = synchronizedSet(new HashSet<>());
11+
12+
ProgressManager(SnykExtendedLanguageClient lc) {
13+
this.lc = lc;
14+
}
15+
16+
public void cancelAll() {
17+
HashSet<String> copy = new HashSet<>(progresses);
18+
for (String token : copy) {
19+
cancelProgress(token);
20+
}
21+
}
22+
23+
public void cancelProgress(String token) {
24+
lc.cancelProgress(token);
25+
progresses.remove(token);
26+
}
27+
28+
public void addProgress(String token) {
29+
progresses.add(token);
30+
}
31+
32+
public void removeProgress(String token) {
33+
progresses.remove(token);
34+
}
14935

15036
}

plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@
4444
import org.eclipse.lsp4e.LanguageClientImpl;
4545
import org.eclipse.lsp4j.ExecuteCommandParams;
4646
import org.eclipse.lsp4j.ProgressParams;
47+
import org.eclipse.lsp4j.WorkDoneProgressCancelParams;
4748
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
4849
import org.eclipse.lsp4j.WorkDoneProgressEnd;
50+
import org.eclipse.lsp4j.WorkDoneProgressKind;
4951
import org.eclipse.lsp4j.WorkDoneProgressNotification;
5052
import org.eclipse.lsp4j.jsonrpc.messages.Either;
5153
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
@@ -92,10 +94,12 @@
9294

9395
@SuppressWarnings("restriction")
9496
public class SnykExtendedLanguageClient extends LanguageClientImpl {
95-
private ProgressManager progressMgr = new ProgressManager();
97+
private ProgressManager progressManager = new ProgressManager(this);
9698
private final ObjectMapper om = new ObjectMapper();
9799
private AnalyticsSender analyticsSender;
98100
private ISnykToolView toolView;
101+
// this field is for testing only
102+
private LanguageServer ls;
99103

100104
private static SnykExtendedLanguageClient instance = null;
101105

@@ -137,6 +141,9 @@ public static SnykExtendedLanguageClient getInstance() {
137141
}
138142

139143
public LanguageServer getConnectedLanguageServer() {
144+
if (this.ls != null) {
145+
return ls;
146+
}
140147
return super.getLanguageServer();
141148
}
142149

@@ -512,30 +519,8 @@ public void reportAnalytics(AbstractAnalyticsEvent event) {
512519

513520
@Override
514521
public CompletableFuture<Void> createProgress(WorkDoneProgressCreateParams params) {
515-
return getProgressMgr().createProgress(params);
516-
}
517-
518-
@Override
519-
public void notifyProgress(ProgressParams params) {
520-
getProgressMgr().updateProgress(params);
521-
}
522-
523-
public void cancelAllProgresses() {
524-
if (getProgressMgr() == null) {
525-
return;
526-
}
527-
CompletableFuture.runAsync(() -> {
528-
for (var progressHashMap : getProgressMgr().progresses.entrySet()) {
529-
var progressToken = progressHashMap.getKey();
530-
WorkDoneProgressEnd workDoneProgressEnd = new WorkDoneProgressEnd();
531-
workDoneProgressEnd.setMessage("Operation canceled.");
532-
Either<WorkDoneProgressNotification, Object> value = Either.forLeft(workDoneProgressEnd);
533-
Either<String, Integer> token = Either.forLeft(progressToken);
534-
535-
var progressParam = new ProgressParams(token, value);
536-
notifyProgress(progressParam);
537-
}
538-
});
522+
this.progressManager.addProgress(params.getToken().getLeft());
523+
return super.createProgress(params);
539524
}
540525

541526
private void runSnykWizard() {
@@ -652,11 +637,49 @@ public void clearCache() {
652637

653638
}
654639

655-
public ProgressManager getProgressMgr() {
656-
return progressMgr;
640+
public void setProgressMgr(ProgressManager progressMgr) {
641+
this.progressManager = progressMgr;
657642
}
658643

659-
public void setProgressMgr(ProgressManager progressMgr) {
660-
this.progressMgr = progressMgr;
644+
@Override
645+
public void notifyProgress(final ProgressParams params) {
646+
WorkDoneProgressNotification progressNotification = params.getValue().getLeft();
647+
if (progressNotification != null && progressNotification.getKind() == WorkDoneProgressKind.end) {
648+
this.progressManager.removeProgress(params.getToken().getLeft());
649+
}
650+
super.notifyProgress(params);
651+
}
652+
653+
/**
654+
* This cancels a progress in language server.
655+
* @param token
656+
*/
657+
public void cancelProgress(String token) {
658+
// call language server to cancel
659+
var workDoneProgressCancelParams = new WorkDoneProgressCancelParams();
660+
workDoneProgressCancelParams.setToken(token);
661+
getConnectedLanguageServer().cancelProgress(workDoneProgressCancelParams);
662+
663+
// call notify progress with end message
664+
var progressParam = getEndProgressParam(token);
665+
super.notifyProgress(progressParam);
666+
}
667+
668+
ProgressParams getEndProgressParam(String token) {
669+
WorkDoneProgressEnd workDoneProgressEnd = new WorkDoneProgressEnd();
670+
workDoneProgressEnd.setMessage("Operation canceled.");
671+
Either<WorkDoneProgressNotification, Object> value = Either.forLeft(workDoneProgressEnd);
672+
Either<String, Integer> tokenEither = Either.forLeft(token);
673+
674+
var progressParam = new ProgressParams(tokenEither, value);
675+
return progressParam;
676+
}
677+
678+
public ProgressManager getProgressManager() {
679+
return this.progressManager;
680+
}
681+
682+
public void setLs(LanguageServer ls) {
683+
this.ls = ls;
661684
}
662685
}

tests/src/test/java/io/snyk/eclipse/plugin/views/snyktoolview/BaseTreeNodeTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ void testRemoveChildren() {
7878
node.removeChildren();
7979

8080
TreeNode[] children = node.getChildren();
81-
assertNotNull(children);
82-
assertEquals(0, children.length);
81+
assertNull(children);
8382
}
8483

8584
@Test
@@ -119,7 +118,7 @@ void testReset() {
119118
assertNull(node.getValue());
120119
assertEquals("", node.getText());
121120
assertNull(node.getImageDescriptor());
122-
assertEquals(0, node.getChildren().length);
121+
assertNull(node.getChildren());
123122
}
124123

125124
@Test

0 commit comments

Comments
 (0)