feat(jdtls): support upstream JDTLS via jdtls_path/lombok_path settings#1415
feat(jdtls): support upstream JDTLS via jdtls_path/lombok_path settings#1415MischaPanch merged 6 commits intooraios:mainfrom
Conversation
Add an offline-friendly Java mode for restricted-network/corporate environments. When both ls_specific_settings.java.jdtls_path and lombok_path are set, Serena uses an existing upstream JDTLS install (e.g. brew install jdtls or extracted jdt-language-server-*.tar.gz from download.eclipse.org) and the system JDK 21+ instead of downloading the ~500 MB platform-specific vscode-java VSIX bundle. The default vscode-java VSIX path is unchanged when neither setting is provided. Resolution details: - jdtls_path expects upstream layout (plugins/, config_<platform>/, features/) - The main equinox.launcher jar is located via regex glob, excluding native fragments - Platform-specific config_<platform>/ is auto-selected by PlatformUtils - JDK home is discovered by querying the JVM via 'java -XshowSettings:properties -version' (single source of truth — works around macOS /usr/bin/java stub which can't be resolved via path traversal) - JDK major version is validated >= 21 from the same JVM output - Lombok agent is always attached (lombok_path is mandatory in this mode) - IntelliCode bundle and Gradle distribution are skipped (irrelevant for Serena's symbol-tools workflow; Maven works via JDTLS-bundled m2e, Gradle via ./gradlew or system install through Buildship default discovery) Workspace hash now incorporates the launcher jar path so plan A and the default mode use distinct workspace directories — prevents stale OSGi configs from a prior JDTLS version blocking startup. Refs: oraios#1414
Cover the offline-mode helpers added in the previous commit without requiring a real JDTLS install or Java runtime — subprocess interactions and platform detection are mocked, JDTLS layouts are synthesised in tmp_path. 30 tests across: - _resolve_launcher_jar — picks main launcher, excludes native fragments, selects highest version, errors on empty plugins - _resolve_config_dir — parameterised by platform, errors on missing / unsupported - _inspect_java — parses Temurin 21 and OpenJDK 17 outputs, errors on missing java.home / version / subprocess failure - _resolve_system_jdk — priority chain (java_home setting > JAVA_HOME > which java), version validation, the macOS /usr/bin/java stub case where the JVM reports a different real java.home - _setup_from_existing_install — happy path (gradle/intellicode None as expected) and validation errors - _setup_runtime_dependencies switch — both/one/neither path-pair set Refs: oraios#1414
Windows resolver looks for bin/java.exe, but the helper created bin/java unconditionally — two tests failed on windows-latest. Switch the helper and assertions to a _JAVA_EXE_NAME constant derived from platform.system(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Looks like there are infra problems with CI, unrelated to code changes:
@opcode81 can I ask about rerun jobs? Maybe it would help |
|
@a-simeshin I restarted the jobs |
Well at least Win tests are pass now, but On this image, the Install R language server step can no longer build the That cascades through WhyNo commit in I can fix it withIn - name: Install R sysdeps
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libuv1-devAlternativeKeep the current step but build - name: Install R language server
env:
USE_BUNDLED_LIBUV: "1"
run: Rscript -e "install.packages('languageserver', repos='https://cloud.r-project.org')"Happy to open a separate PR with the workflow fix if it would be helpful |
|
Thanks, the R failure has been around for a few weeks, unrelated to changes here. Yes, a PR to fix the R install failure on ubuntu would be appreciated, thank you! |
|
Thanks for the PR @a-simeshin ! Pls also extend the changelog and our documentation of the java options in 050_configuration.md, in the advanced options section. Generally, I highly recommend using our jetbrains plugin for java development. Apart from being more stable, having more features and introducing less overhead, using the plugin also supports this project. |
…build on ubuntu-24.04)
- CHANGELOG.md: add entry under "Language Servers" describing the new
upstream JDTLS mode (jdtls_path/lombok_path/java_home settings).
- docs/02-usage/050_configuration.md (Java section):
- Add "When to use which mode" decision helper before the settings
table, listing concrete reasons to pick the upstream mode
(restricted networks, on-disk footprint, existing JDTLS install,
security policy).
- Promote the JDK 21+ requirement to a dedicated paragraph and
document the exact JDK resolution order
(java_home setting -> JAVA_HOME env -> first java on PATH).
- Note that vscode-java-only settings (gradle_version,
vscode_java_version, intellicode_*) are silently ignored when
upstream mode is active, so users don't expect them to take effect.
Done! Unfortunately, corporate terms and conditions impose restrictions on many JetBrains plugins. Otherwise I'd really rather not have to configure JDTLS by myself. |
|
Ah, that's a shame. Anything we can do to let the plugin be accepted by your IT department @a-simeshin ? We could give a presentation or sell the plugin via a different channel than the Jetbrains marketplace. The plugin runs fully locally, doesn't have any telemetry or anything security critical. From an IT sec perspective, allowing to run Serena but disallowing the plugin doesn't make sense Feel free to contact us at info@oraios-software.de |
I see a very shady situation here for me and others within the corporate rules. Usually I have to submit any opesource code for internal review, justify down to the last line why it's needed, and then maybe in about six months I'll be able to use it. I waited six months to use IDE themes, I'm not kidding 🥲 Furthermore sometimes companies forks JetBrains IDE and creates its own closed build with closed plugins. This is why I actually switched to Zed and use Serena as an MCP with an approved jdtls version from Eclipse. Someone had already submitted jdtls earlier, after going through a hell of bureaucratic hassle. So this fix is the only one chance to use Serena officially on my office projects. Also I don't know if you're aware of this or not, but jdtls doesn't work correctly with getters and setters generated via lombok, but in your Jetbrains plugin everything works fine. |
MischaPanch
left a comment
There was a problem hiding this comment.
Thanks @a-simeshin, this is close to be merged, just minor questions.
Sorry to hear about your corporate restrictions, sounds really inconvenient
The previous change unconditionally mixed the JDTLS launcher jar path into the workspace md5, which would have invalidated every existing JDTLS workspace on Serena upgrade and forced a one-time cold reindex for users who never opted into upstream-JDTLS mode. Make the launcher path part of the hash only when `jdtls_path` is explicitly set, so default-route users get the exact pre-upstream format (`md5(repository_root_path)`) and reuse their caches as before. Upstream mode keeps the launcher path in the hash to isolate it from the default workspace and from other upstream installations. Extract the hash computation into a small static helper and add unit tests covering both branches, the backwards-compatibility contract, and default/upstream isolation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #1414
Activation
Both paths set → upstream mode (no downloads). Exactly one set → init fails with a clear message. Neither set → unchanged behaviour, vscode-java VSIX is downloaded as today (default code path is byte-for-byte identical).
Resolution details
jdtls_pathexpects the upstream layout (plugins/,config_<platform>/,features/).org.eclipse.equinox.launcher_*.jaris located via regex glob, excluding the platform-specific native fragments (*.cocoa.macosx.*,*.gtk.linux.*,*.win32.win32.*).config_<platform>/is auto-selected fromPlatformUtils.get_platform_id().java -XshowSettings:properties -version. This is the single source of truth and works around the macOS/usr/bin/javastub case, whereparent.parentof the locator yields/usr(system root) instead of the real JDK home; the JVM correctly reports its ownjava.homeregardless.>= 21from the same JVM output.-javaagent:<lombok_path>); harmless when the project doesn't use Lombok../gradlewin the project, or system Gradle via~/.gradle/. Maven projects work via JDTLS-bundled m2e (Maven Embedder).Workspace hash bugfix (included)
Independent of the main feature,
EclipseJDTLS.create_launch_command()previously derived the workspace directory hash only from the project path. Theconfig_pathdirectory inside the workspace is copied from the readonly OSGi config on first launch and reused thereafter. When the JDTLS launcher is upgraded (defaultvscode_java_versionbump, or switching modes), the stale config still references bundles from the previous launcher version, and the new launcher hangs oninitializeuntil the request times out.This PR includes the launcher jar path in the workspace-hash input so a launcher upgrade or mode switch produces a fresh
config_path. Happy to split this into a separate PR if preferred.Tests
A new file
test/solidlsp/java/test_jdtls_path_resolution.pycovers the resolution helpers without requiring a real JDTLS install or Java runtime — subprocess interactions and platform detection are mocked, JDTLS layouts are synthesised intmp_path. Coverage:_resolve_launcher_jar— picks main launcher, excludes native fragments, selects highest version, errors on empty plugins_resolve_config_dir— parameterised by platform, errors on missing / unsupported_inspect_java— parses Temurin 21 and OpenJDK 17 outputs, errors on missingjava.home/ version / subprocess failure_resolve_system_jdk— priority chain (java_homesetting >JAVA_HOME>which java), version validation, the macOS/usr/bin/javastub case_setup_from_existing_install— happy path (gradle / intellicodeNoneas expected) and validation errors_setup_runtime_dependenciesswitch — both/one/neither path-pair setDefault-mode integration tests in
test/solidlsp/java/test_java_basic.pyare unchanged and still cover the vscode-java VSIX path end-to-end.Local checks:
ruff check,ruff format --check,mypy(src + test),pyteston the new file all pass.Open design questions
use_upstream_jdtls: true+ paths) would be more obvious but adds one more setting. Happy to switch if maintainers prefer.