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

Commit

Permalink
Add the Java DAP implementation (+LSP uprev) (#258)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lhchavez authored Jun 9, 2021
1 parent 0bb3692 commit 7bb14ca
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 18 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
!extra/cquery11
!extra/fluxbox/apps
!extra/fluxbox/init
!extra/java-dap
!extra/lang-gitignore
!extra/matplotlibrc
!extra/polygott-gitignore
Expand Down
140 changes: 140 additions & 0 deletions extra/java-dap
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""Small wrapper to correctly initialize the Java DAP.
This launches the (normal) Java LSP and then tells it to initialize with the
Java DAP plugin bundle. This causes the DAP plugin to bind to a TCP port, and
once that's done, the communication with the DAP can start.
This is known to be flaky, so this retries until it succeeds.
"""

import json
import logging
import os
import signal
import subprocess
import sys
import time

from typing import Any, IO, Dict, List, Optional

_JAVA_DAP_BUNDLE = '/run_dir/com.microsoft.java.debug.plugin-0.32.0.jar'


def _send_lsp_message(msg: Dict[str, Any], lsp: IO[bytes]) -> None:
"""Sends one LSP message."""
serialized_msg = json.dumps({
'jsonrpc': '2.0',
**msg,
})
payload = len(serialized_msg)
lsp.write((f'Content-Length: {len(serialized_msg)}\r\n\r\n' +
serialized_msg).encode('utf-8'))
lsp.flush()


def _receive_lsp_message(lsp: IO[bytes]) -> Optional[Dict[str, Any]]:
"""Receives one LSP message."""
headers = b''
while not headers.endswith(b'\r\n\r\n'):
byte = lsp.read(1)
if len(byte) == 0:
return None
headers += byte
content_length = 0
for header in headers.strip().split(b'\r\n'):
name, value = header.split(b':', maxsplit=2)
if name.strip().lower() == b'content-length':
content_length = int(value.strip())
serialized = b''
while content_length:
chunk = lsp.read(content_length)
if not chunk:
raise Exception(f'short read: {serialized!r}')
content_length -= len(chunk)
serialized += chunk
return json.loads(serialized)


def _run() -> bool:
"""Attempts to start the DAP. Returns whether the caller should retry."""
with subprocess.Popen(['/usr/bin/run-language-server', '-l', 'java'],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
preexec_fn=os.setsid) as dap:
try:
_send_lsp_message(
{
'id': 1,
'method': 'initialize',
'params': {
'processId': None,
'initializationOptions': {
'bundles': [
_JAVA_DAP_BUNDLE,
],
},
'trace': 'verbose',
'capabilities': {},
},
}, dap.stdin)
# Wait for the initialize message has been acknowledged.
# This maximizes the probability of success.
while True:
message = _receive_lsp_message(dap.stdout)
if not message:
return True
if message.get('method') == 'window/logMessage':
print(message.get('params', {}).get('message'),
file=sys.stderr)
if message.get('id') == 1:
break
_send_lsp_message(
{
'id': 2,
'method': 'workspace/executeCommand',
'params': {
'command': 'vscode.java.startDebugSession',
},
}, dap.stdin)
# Wait for the reply. If the request errored out, exit early to
# send a clear signal to the caller.
while True:
message = _receive_lsp_message(dap.stdout)
if not message:
return True
if message.get('method') == 'window/logMessage':
print(message.get('params', {}).get('message'),
file=sys.stderr)
if message.get('id') == 2:
if 'error' in message:
print(message['error'].get('message'), file=sys.stderr)
# This happens often during the first launch before
# things warm up.
return True
break
# Keep reading to drain the queue.
while True:
message = _receive_lsp_message(dap.stdout)
if not message:
break
if message.get('method') == 'window/logMessage':
print(message.get('params', {}).get('message'),
file=sys.stderr)
except Exception:
logging.exception('failed')
finally:
pgrp = os.getpgid(dap.pid)
os.killpg(pgrp, signal.SIGINT)
return False


def _main() -> None:
while True:
retry = _run()
if not retry:
break


if __name__ == '__main__':
_main()
3 changes: 3 additions & 0 deletions gen/Dockerfile.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ COPY ./run_dir /run_dir/
RUN ln -sf /usr/lib/chromium-browser/chromedriver /usr/local/bin

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

COPY ./extra/_test_runner.py /home/runner/_test_runner.py
COPY ./extra/cquery11 /opt/homes/cpp11/.cquery
Expand Down
2 changes: 1 addition & 1 deletion gen/run-language-server.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ case "$LANGUAGE" in
<% for ( let lang of languages ) { -%>
<%- lang.names.map(x => `"${x}"`).join('|') %>)
<% if ( lang.languageServer ) { -%>
<%- c(lang.languageServer.command) %>
exec <%- c(lang.languageServer.command) %>
<% } else { -%>
echo "No language server configured for <%= lang.name %>" >&2
exit 1
Expand Down
5 changes: 3 additions & 2 deletions languages/java.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ packages = [
"openjdk-11-jdk"
]
setup = [
"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",
"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",
"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",
"mvn -f /tmp/emptypom.xml -Dmaven.repo.local=/home/runner/.m2/repository de.qaware.maven:go-offline-maven-plugin:resolve-dependencies dependency:copy-dependencies",
"rm /tmp/emptypom.xml"
Expand Down Expand Up @@ -43,11 +43,12 @@ command = [
"-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.5.0.v20180512-1130.jar",
"/config/language-server/plugins/org.eclipse.equinox.launcher_1.6.100.v20201223-0822.jar",
"-configuration",
"/config/language-server/config_linux",
"-data",
Expand Down
3 changes: 3 additions & 0 deletions out/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,9 @@ COPY ./run_dir /run_dir/
RUN ln -sf /usr/lib/chromium-browser/chromedriver /usr/local/bin

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

COPY ./extra/_test_runner.py /home/runner/_test_runner.py
COPY ./extra/cquery11 /opt/homes/cpp11/.cquery
Expand Down
28 changes: 14 additions & 14 deletions out/run-language-server
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ case "$LANGUAGE" in
exit 1
;;
"java")
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
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
;;
"ballerina")
echo "No language server configured for ballerina" >&2
Expand All @@ -34,7 +34,7 @@ case "$LANGUAGE" in
exit 1
;;
"c")
cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery"}'
exec cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery"}'
;;
"common lisp"|"clisp"|"lisp")
echo "No language server configured for common lisp" >&2
Expand All @@ -45,10 +45,10 @@ case "$LANGUAGE" in
exit 1
;;
"cpp"|"c++")
cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++17", "-pthread"]}'
exec cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++17", "-pthread"]}'
;;
"cpp11")
cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++11", "-pthread"]}'
exec cquery '--init={"progressReportFrequencyMs": -1,"cacheDirectory":"/tmp/cquery", "extraClangArguments": ["-std=c++11", "-pthread"]}'
;;
"crystal")
echo "No language server configured for crystal" >&2
Expand All @@ -63,10 +63,10 @@ case "$LANGUAGE" in
exit 1
;;
"dart")
/usr/lib/dart/bin/dart /usr/lib/dart/bin/snapshots/analysis_server.dart.snapshot --lsp
exec /usr/lib/dart/bin/dart /usr/lib/dart/bin/snapshots/analysis_server.dart.snapshot --lsp
;;
"deno")
deno lsp
exec deno lsp
;;
"elisp")
echo "No language server configured for elisp" >&2
Expand All @@ -93,7 +93,7 @@ case "$LANGUAGE" in
exit 1
;;
"flow")
flow-language-server --stdio
exec flow-language-server --stdio
;;
"forth")
echo "No language server configured for forth" >&2
Expand All @@ -112,7 +112,7 @@ case "$LANGUAGE" in
exit 1
;;
"go"|"golang")
/bin/bash -c /opt/homes/go/go/bin/bingo
exec /bin/bash -c /opt/homes/go/go/bin/bingo
;;
"guile"|"scheme")
echo "No language server configured for guile" >&2
Expand All @@ -123,7 +123,7 @@ case "$LANGUAGE" in
exit 1
;;
"haxe")
haxe --server-listen stdio
exec haxe --server-listen stdio
;;
"jest")
echo "No language server configured for jest" >&2
Expand Down Expand Up @@ -186,16 +186,16 @@ case "$LANGUAGE" in
exit 1
;;
"python3")
pyls -v
exec pyls -v
;;
"pygame")
pyls -v
exec pyls -v
;;
"python")
pyls -v
exec pyls -v
;;
"pyxel")
pyls -v
exec pyls -v
;;
"quil")
echo "No language server configured for quil" >&2
Expand All @@ -222,7 +222,7 @@ case "$LANGUAGE" in
exit 1
;;
"ruby")
solargraph stdio
exec solargraph stdio
;;
"rust")
echo "No language server configured for rust" >&2
Expand Down
2 changes: 1 addition & 1 deletion out/share/polygott/phase2.d/java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ chown -R $(id -u):$(id -g) /home/runner
echo 'Setup java'
cd "${HOME}"

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
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
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
mvn -f /tmp/emptypom.xml -Dmaven.repo.local=/home/runner/.m2/repository de.qaware.maven:go-offline-maven-plugin:resolve-dependencies dependency:copy-dependencies
rm /tmp/emptypom.xml
Expand Down
Binary file not shown.

0 comments on commit 7bb14ca

Please sign in to comment.