Skip to content
This repository was archived by the owner on May 4, 2022. It is now read-only.

Commit 056a563

Browse files
committed
Add the Java DAP implementation (+LSP uprev)
This change bundles a pre-built binary of the Java DAP with microsoft/java-debug#379 and replit/java-debug@2a556e5 applied, so that we can specify what host/port it should bind to, in addition to always using localhost as the interface that it binds to. In order for this to function correctly, it also needs to be running a more recent version of the Java LSP, so we will upgrade that too.
1 parent 0bb3692 commit 056a563

File tree

9 files changed

+166
-18
lines changed

9 files changed

+166
-18
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
!extra/cquery11
1010
!extra/fluxbox/apps
1111
!extra/fluxbox/init
12+
!extra/java-dap
1213
!extra/lang-gitignore
1314
!extra/matplotlibrc
1415
!extra/polygott-gitignore

extra/java-dap

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python3
2+
"""Small wrapper to correctly initialize the Java DAP.
3+
4+
This launches the (normal) Java LSP and then tells it to initialize with the
5+
Java DAP plugin bundle. This causes the DAP plugin to bind to a TCP port, and
6+
once that's done, the communication with the DAP can start.
7+
8+
This is known to be flaky, so this retries until it succeeds.
9+
"""
10+
11+
import json
12+
import logging
13+
import os
14+
import signal
15+
import subprocess
16+
import sys
17+
import time
18+
19+
from typing import Any, IO, Dict, List, Optional
20+
21+
_JAVA_DAP_BUNDLE = '/run_dir/com.microsoft.java.debug.plugin-0.32.0.jar'
22+
23+
24+
def _send_lsp_message(msg: Dict[str, Any], lsp: IO[bytes]) -> None:
25+
"""Sends one LSP message."""
26+
serialized_msg = json.dumps({
27+
'jsonrpc': '2.0',
28+
**msg,
29+
})
30+
payload = len(serialized_msg)
31+
lsp.write((f'Content-Length: {len(serialized_msg)}\r\n\r\n' +
32+
serialized_msg).encode('utf-8'))
33+
lsp.flush()
34+
35+
36+
def _receive_lsp_message(lsp: IO[bytes]) -> Optional[Dict[str, Any]]:
37+
"""Receives one LSP message."""
38+
headers = b''
39+
while not headers.endswith(b'\r\n\r\n'):
40+
byte = lsp.read(1)
41+
if len(byte) == 0:
42+
return None
43+
headers += byte
44+
content_length = 0
45+
for header in headers.strip().split(b'\r\n'):
46+
name, value = header.split(b':', maxsplit=2)
47+
if name.strip().lower() == b'content-length':
48+
content_length = int(value.strip())
49+
serialized = b''
50+
while content_length:
51+
chunk = lsp.read(content_length)
52+
if not chunk:
53+
raise Exception(f'short read: {serialized!r}')
54+
content_length -= len(chunk)
55+
serialized += chunk
56+
return json.loads(serialized)
57+
58+
59+
def _run() -> bool:
60+
"""Attempts to start the DAP. Returns whether the caller should retry."""
61+
with subprocess.Popen(['/usr/bin/run-language-server', '-l', 'java'],
62+
stdout=subprocess.PIPE,
63+
stdin=subprocess.PIPE,
64+
preexec_fn=os.setsid) as dap:
65+
try:
66+
_send_lsp_message(
67+
{
68+
'id': 1,
69+
'method': 'initialize',
70+
'params': {
71+
'processId': None,
72+
'initializationOptions': {
73+
'bundles': [
74+
_JAVA_DAP_BUNDLE,
75+
],
76+
},
77+
'trace': 'verbose',
78+
'capabilities': {},
79+
},
80+
}, dap.stdin)
81+
# Wait for the initialize message has been acknowledged.
82+
# This maximizes the probability of success.
83+
while True:
84+
message = _receive_lsp_message(dap.stdout)
85+
if not message:
86+
return True
87+
if message.get('method') == 'window/logMessage':
88+
print(message.get('params', {}).get('message'),
89+
file=sys.stderr)
90+
if message.get('id') == 1:
91+
break
92+
_send_lsp_message(
93+
{
94+
'id': 2,
95+
'method': 'workspace/executeCommand',
96+
'params': {
97+
'command': 'vscode.java.startDebugSession',
98+
},
99+
}, dap.stdin)
100+
# Wait for the reply. If the request errored out, exit early to
101+
# send a clear signal to the caller.
102+
while True:
103+
message = _receive_lsp_message(dap.stdout)
104+
if not message:
105+
return True
106+
if message.get('method') == 'window/logMessage':
107+
print(message.get('params', {}).get('message'),
108+
file=sys.stderr)
109+
if message.get('id') == 2:
110+
if 'error' in message:
111+
print(message['error'].get('message'), file=sys.stderr)
112+
# This happens often during the first launch before
113+
# things warm up.
114+
return True
115+
break
116+
# Keep reading to drain the queue.
117+
while True:
118+
message = _receive_lsp_message(dap.stdout)
119+
if not message:
120+
break
121+
if message.get('method') == 'window/logMessage':
122+
print(message.get('params', {}).get('message'),
123+
file=sys.stderr)
124+
except Exception:
125+
logging.exception('failed')
126+
finally:
127+
pgrp = os.getpgid(dap.pid)
128+
os.killpg(pgrp, signal.SIGINT)
129+
return False
130+
131+
132+
def _main() -> None:
133+
while True:
134+
retry = _run()
135+
if not retry:
136+
break
137+
138+
139+
if __name__ == '__main__':
140+
_main()

gen/Dockerfile.ejs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ COPY ./run_dir /run_dir/
9595
RUN ln -sf /usr/lib/chromium-browser/chromedriver /usr/local/bin
9696

9797
COPY ./extra/apt-install /usr/bin/install-pkg
98+
RUN mkdir -p /opt/dap/java/
99+
COPY ./extra/java-dap /opt/dap/java/run
100+
RUN chmod +x /opt/dap/java/run
98101

99102
COPY ./extra/_test_runner.py /home/runner/_test_runner.py
100103
COPY ./extra/cquery11 /opt/homes/cpp11/.cquery

gen/run-language-server.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ case "$LANGUAGE" in
2121
<% for ( let lang of languages ) { -%>
2222
<%- lang.names.map(x => `"${x}"`).join('|') %>)
2323
<% if ( lang.languageServer ) { -%>
24-
<%- c(lang.languageServer.command) %>
24+
exec <%- c(lang.languageServer.command) %>
2525
<% } else { -%>
2626
echo "No language server configured for <%= lang.name %>" >&2
2727
exit 1

languages/java.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ packages = [
88
"openjdk-11-jdk"
99
]
1010
setup = [
11-
"mkdir -p /config/language-server && cd /config/language-server && wget http://download.eclipse.org/jdtls/milestones/0.21.0/jdt-language-server-0.21.0-201806152234.tar.gz && tar -xzf jdt-language-server-0.21.0-201806152234.tar.gz && rm jdt-language-server-0.21.0-201806152234.tar.gz && chown runner:runner -R /config/language-server",
11+
"mkdir -p /config/language-server && cd /config/language-server && wget https://download.eclipse.org/jdtls/milestones/1.1.2/jdt-language-server-1.1.2-202105191944.tar.gz && tar -xzf jdt-language-server-1.1.2-202105191944.tar.gz && rm jdt-language-server-1.1.2-202105191944.tar.gz && chown runner:runner -R /config/language-server",
1212
"echo '<project> <modelVersion>4.0.0</modelVersion> <groupId>mygroupid</groupId> <artifactId>myartifactid</artifactId> <version>0.0-SNAPSHOT</version> <build><plugins> <plugin> <groupId>de.qaware.maven</groupId> <artifactId>go-offline-maven-plugin</artifactId> <version>1.2.5</version> <configuration> <dynamicDependencies> <DynamicDependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit4</artifactId> <version>2.20.1</version> <repositoryType>PLUGIN</repositoryType> </DynamicDependency> <DynamicDependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>4.2.1</version> <classifier>jpa</classifier> <repositoryType>MAIN</repositoryType> </DynamicDependency> </dynamicDependencies> </configuration> </plugin> </plugins></build> </project>' > /tmp/emptypom.xml",
1313
"mvn -f /tmp/emptypom.xml -Dmaven.repo.local=/home/runner/.m2/repository de.qaware.maven:go-offline-maven-plugin:resolve-dependencies dependency:copy-dependencies",
1414
"rm /tmp/emptypom.xml"
@@ -43,11 +43,12 @@ command = [
4343
"-Declipse.application=org.eclipse.jdt.ls.core.id1",
4444
"-Dosgi.bundles.defaultStartLevel=4",
4545
"-Declipse.product=org.eclipse.jdt.ls.core.product",
46+
"-Dcom.microsoft.java.debug.serverAddress=localhost:41010",
4647
"-noverify",
4748
"-Xmx256m",
4849
"-XX:+UseConcMarkSweepGC",
4950
"-jar",
50-
"/config/language-server/plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar",
51+
"/config/language-server/plugins/org.eclipse.equinox.launcher_1.6.100.v20201223-0822.jar",
5152
"-configuration",
5253
"/config/language-server/config_linux",
5354
"-data",

out/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,9 @@ COPY ./run_dir /run_dir/
817817
RUN ln -sf /usr/lib/chromium-browser/chromedriver /usr/local/bin
818818

819819
COPY ./extra/apt-install /usr/bin/install-pkg
820+
RUN mkdir -p /opt/dap/java/
821+
COPY ./extra/java-dap /opt/dap/java/run
822+
RUN chmod +x /opt/dap/java/run
820823

821824
COPY ./extra/_test_runner.py /home/runner/_test_runner.py
822825
COPY ./extra/cquery11 /opt/homes/cpp11/.cquery

out/run-language-server

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ case "$LANGUAGE" in
2323
exit 1
2424
;;
2525
"java")
26-
java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -noverify -Xmx256m -XX:+UseConcMarkSweepGC -jar /config/language-server/plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar -configuration /config/language-server/config_linux -data /home/runner
26+
exec java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Dcom.microsoft.java.debug.serverAddress=localhost:41010 -noverify -Xmx256m -XX:+UseConcMarkSweepGC -jar /config/language-server/plugins/org.eclipse.equinox.launcher_1.6.100.v20201223-0822.jar -configuration /config/language-server/config_linux -data /home/runner
2727
;;
2828
"ballerina")
2929
echo "No language server configured for ballerina" >&2
@@ -34,7 +34,7 @@ case "$LANGUAGE" in
3434
exit 1
3535
;;
3636
"c")
37-
cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery"}'
37+
exec cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery"}'
3838
;;
3939
"common lisp"|"clisp"|"lisp")
4040
echo "No language server configured for common lisp" >&2
@@ -45,10 +45,10 @@ case "$LANGUAGE" in
4545
exit 1
4646
;;
4747
"cpp"|"c++")
48-
cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++17", "-pthread"]}'
48+
exec cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++17", "-pthread"]}'
4949
;;
5050
"cpp11")
51-
cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++11", "-pthread"]}'
51+
exec cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++11", "-pthread"]}'
5252
;;
5353
"crystal")
5454
echo "No language server configured for crystal" >&2
@@ -63,10 +63,10 @@ case "$LANGUAGE" in
6363
exit 1
6464
;;
6565
"dart")
66-
/usr/lib/dart/bin/dart /usr/lib/dart/bin/snapshots/analysis_server.dart.snapshot --lsp
66+
exec /usr/lib/dart/bin/dart /usr/lib/dart/bin/snapshots/analysis_server.dart.snapshot --lsp
6767
;;
6868
"deno")
69-
deno lsp
69+
exec deno lsp
7070
;;
7171
"elisp")
7272
echo "No language server configured for elisp" >&2
@@ -93,7 +93,7 @@ case "$LANGUAGE" in
9393
exit 1
9494
;;
9595
"flow")
96-
flow-language-server --stdio
96+
exec flow-language-server --stdio
9797
;;
9898
"forth")
9999
echo "No language server configured for forth" >&2
@@ -112,7 +112,7 @@ case "$LANGUAGE" in
112112
exit 1
113113
;;
114114
"go"|"golang")
115-
/bin/bash -c /opt/homes/go/go/bin/bingo
115+
exec /bin/bash -c /opt/homes/go/go/bin/bingo
116116
;;
117117
"guile"|"scheme")
118118
echo "No language server configured for guile" >&2
@@ -123,7 +123,7 @@ case "$LANGUAGE" in
123123
exit 1
124124
;;
125125
"haxe")
126-
haxe --server-listen stdio
126+
exec haxe --server-listen stdio
127127
;;
128128
"jest")
129129
echo "No language server configured for jest" >&2
@@ -186,16 +186,16 @@ case "$LANGUAGE" in
186186
exit 1
187187
;;
188188
"python3")
189-
pyls -v
189+
exec pyls -v
190190
;;
191191
"pygame")
192-
pyls -v
192+
exec pyls -v
193193
;;
194194
"python")
195-
pyls -v
195+
exec pyls -v
196196
;;
197197
"pyxel")
198-
pyls -v
198+
exec pyls -v
199199
;;
200200
"quil")
201201
echo "No language server configured for quil" >&2
@@ -222,7 +222,7 @@ case "$LANGUAGE" in
222222
exit 1
223223
;;
224224
"ruby")
225-
solargraph stdio
225+
exec solargraph stdio
226226
;;
227227
"rust")
228228
echo "No language server configured for rust" >&2

out/share/polygott/phase2.d/java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ chown -R $(id -u):$(id -g) /home/runner
1010
echo 'Setup java'
1111
cd "${HOME}"
1212

13-
mkdir -p /config/language-server && cd /config/language-server && wget http://download.eclipse.org/jdtls/milestones/0.21.0/jdt-language-server-0.21.0-201806152234.tar.gz && tar -xzf jdt-language-server-0.21.0-201806152234.tar.gz && rm jdt-language-server-0.21.0-201806152234.tar.gz && chown runner:runner -R /config/language-server
13+
mkdir -p /config/language-server && cd /config/language-server && wget https://download.eclipse.org/jdtls/milestones/1.1.2/jdt-language-server-1.1.2-202105191944.tar.gz && tar -xzf jdt-language-server-1.1.2-202105191944.tar.gz && rm jdt-language-server-1.1.2-202105191944.tar.gz && chown runner:runner -R /config/language-server
1414
echo '<project> <modelVersion>4.0.0</modelVersion> <groupId>mygroupid</groupId> <artifactId>myartifactid</artifactId> <version>0.0-SNAPSHOT</version> <build><plugins> <plugin> <groupId>de.qaware.maven</groupId> <artifactId>go-offline-maven-plugin</artifactId> <version>1.2.5</version> <configuration> <dynamicDependencies> <DynamicDependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit4</artifactId> <version>2.20.1</version> <repositoryType>PLUGIN</repositoryType> </DynamicDependency> <DynamicDependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>4.2.1</version> <classifier>jpa</classifier> <repositoryType>MAIN</repositoryType> </DynamicDependency> </dynamicDependencies> </configuration> </plugin> </plugins></build> </project>' > /tmp/emptypom.xml
1515
mvn -f /tmp/emptypom.xml -Dmaven.repo.local=/home/runner/.m2/repository de.qaware.maven:go-offline-maven-plugin:resolve-dependencies dependency:copy-dependencies
1616
rm /tmp/emptypom.xml
Binary file not shown.

0 commit comments

Comments
 (0)