java-language-server
java-language-server copied to clipboard
Standalone Version
Would it be possible to separate out just the langserver so that it can be used with other clients?
mvn package
will build a standalone fat-jar that is the language server. The only real difficulty with getting it working with other clients is that users are allowed to set a couple things in .vscode/settings.json which are passed in here, and that isn't a very well-specified aspect of the language server protocol.
That command fails for me with the following.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; nested exception is java.lang.reflect.InvocationTargetException: null
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.NoClassDefFoundError: workspace/out/com/example/Test (wrong name: com/example/Test)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.apache.maven.surefire.util.DefaultScanResult.loadClass(DefaultScanResult.java:131)
at org.apache.maven.surefire.util.DefaultScanResult.applyFilter(DefaultScanResult.java:95)
at org.apache.maven.surefire.junit4.JUnit4Provider.scanClassPath(JUnit4Provider.java:194)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:92)
... 9 more
Very odd. Just re-cloned and ran mvn test and it worked. Try again on latest master?
Latest master:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; nested exception is java.lang.reflect.InvocationTargetException: null
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.NoClassDefFoundError: workspace/out/com/example/Test (wrong name: com/example/Test)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.apache.maven.surefire.util.DefaultScanResult.loadClass(DefaultScanResult.java:131)
at org.apache.maven.surefire.util.DefaultScanResult.applyFilter(DefaultScanResult.java:95)
at org.apache.maven.surefire.junit4.JUnit4Provider.scanClassPath(JUnit4Provider.java:194)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:92)
... 9 more
Results :
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
I think surefire is finding a test resource named Test.class
and that's messing it up. It seems that class is no longer in use anyway: https://github.com/georgewfraser/vscode-javac/commit/d6e42f1810abf59078e853b7cca46ca61e2980ee
Try again please 😅
Running with d6e42f1
-------------------------------------------------------
T E S T S
-------------------------------------------------------
org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; nested exception is java.lang.reflect.InvocationTargetException: null
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.NoClassDefFoundError: workspace/out/com/example/Test (wrong name: com/example/Test)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.apache.maven.surefire.util.DefaultScanResult.loadClass(DefaultScanResult.java:131)
at org.apache.maven.surefire.util.DefaultScanResult.applyFilter(DefaultScanResult.java:95)
at org.apache.maven.surefire.junit4.JUnit4Provider.scanClassPath(JUnit4Provider.java:194)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:92)
... 9 more
Results :
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
🤷♂️
I got in working on CircleCI which may or may not be relevant to your issue https://circleci.com/gh/georgewfraser/vscode-javac
I think you probably still have the file src/test/test-project/workspace/com/example/Test.class, even though it was deleted upstream. Re-cloning the repo might do the trick.
That worked!
Hi @georgewfraser. First off, thanks for all of your hard work in making this library, it's incredibly useful!
I'm also interested in running this as a standalone JAR but I'm having a bit of trouble.
tl;dr: If you give me a 1-liner for running this as a standalone process I'd be much appreciative.
The following describes my attempt to do so.
Attempting to run from command line
As you suggest above, I ran mvn package
to build out/fat-jar.jar
.
I see from Main.ts that you seem to run the external process with:
$ java -cp out/fat-jar.jar -Djavacs.port=55282 -Xverify:none org.javacs.Main
However, when I run this from the repo root I get:
Dec 09, 2017 11:15:59 AM org.javacs.Main main
SEVERE: Failed
java.lang.RuntimeException: java.net.MalformedURLException: no protocol: out/fat-jar.jar
at org.javacs.ChildFirstClassLoader.parse(ChildFirstClassLoader.java:34)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
...
Caused by: java.net.MalformedURLException: no protocol: out/fat-jar.jar
at java.net.URL.<init>(URL.java:593)
I'm on macOS running Java 1.8.0_92.
Initial troubleshooting
Based on the error message it appears that it's attempting to parseout/fat-jar.jar
as a URL but failing because there is no protocol specified.
Adding a protocol:
$ java -cp file://out/fat-jar.jar -Djavacs.port=55282 org.javacs.Main
Error: Could not find or load main class org.javacs.Main
This is expected, as Java doesn't expect you to use file://
when specifying a classpath.
Tracing execution flow
vscode-javac/src/main/java/org/javacs/Main.java
// Execution starts in main() where LangTools.createLangToolsClassLoader() is called
public static void main(String[] args) {
try {
ClassLoader langTools = LangTools.createLangToolsClassLoader();
vscode-javac/src/main/java/org/javacs/LangTools.java
// Which then calls ChildFirstClassLoader.fromClassPath, passing in the provided `classPath`
public static ClassLoader createLangToolsClassLoader() {
String classPath = System.getProperty("java.class.path");
...
return ChildFirstClassLoader.fromClassPath(classPath, LANGTOOLS_PACKAGES, parent);
vscode-javac/src/main/java/org/javacs/ChildFirstClassLoader.java
// When creating a new ChildFirstClassLoader `parseClassPath` is called with the
// supplied classPath
static ChildFirstClassLoader fromClassPath(
String classPath, String[] packages, ClassLoader parent) {
return new ChildFirstClassLoader(parseClassPath(classPath), packages, parent);
}
Still ChildFirstClassLoader.java
// This method calls ChildFirstClassLoader.parse on all provided classPath
// (split by File.pathSeparator))
static URL[] parseClassPath(String classPath) {
return Arrays.stream(classPath.split(File.pathSeparator))
.map(ChildFirstClassLoader::parse)
.toArray(URL[]::new);
}
Still ChildFirstClassLoader.java
// Here's the issue: we specified "out/fat-jar.jar" as our class path on the command line,
// since it doesn't start with a "/", it'll try to create a new URL from
// "out/fat-jar.jar" directly. This will fail as there's no protocol, giving us the error
// we saw earlier.
private static URL parse(String urlString) {
try {
if (urlString.startsWith("/")) return Paths.get(urlString).toUri().toURL();
else return new URL(urlString);
...
Summary
Trying to run this tool from the command line via:
$ java -cp out/fat-jar.jar -Djavacs.port=55282 org.javacs.Main
fails because vscode-javac/src/main/java/org/javacs/ChildFirstClassLoader.java::parse
tries to construct a URL from out/fat-jar.jar
, which doesn't specify a protocol.
Thoughts?
I'd be happy to write a brief blurb in the README or Wiki on running this from the command line once I know how.
@clintgibler There's some classloader hackery to force java to load our own copy of javac rather than the built-in one on the user's system. I think you can make this work just by fixing the parse function to recognize files in a more general way, something like
try { new Url(...) }
catch (MalformedURLException) { Paths.get(...) }
Thanks for the quick response!
I've made a 4 line change, see here, that yields:
$ java -cp out/fat-jar.jar -Djavacs.port=55282 org.javacs.Main
Dec 11, 2017 8:16:55 PM org.javacs.ChildFirstClassLoader loadClass
WARNING: Couldn't find org.javacs.Main in child class loader
2017-12-11 20:16:55 INFO org.javacs.Main connectToNode Connecting to 55282
2017-12-11 20:16:55 SEVERE org.javacs.Main run Connection refused
java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
My initial thought was that it might be working but I just didn't have anything ready listening on that port, but when I start nc
on localhost
55282 I get the same result.
Thoughts?
The java process expects someone to be listening, on the indicated port. The other side of this port is here
It would probably better to just refactor to use stdio as the only option, it's simpler for standalone use cases
OK cool, thanks!
I'd also welcome an out-of-the-box working standalone version. The eclipse language server protocol implementation is way too monolithic for my liking. When trying to run the compiled fat-jar.jar
, I get the same error as @clintgibler. I'll try to apply the mentioned adjustments.
$ java -jar out/fat-jar.jar
Mar 08, 2018 11:28:27 AM org.javacs.Main main
SEVERE: Failed
java.lang.RuntimeException: java.net.MalformedURLException: no protocol: out/fat-jar.jar
at org.javacs.ChildFirstClassLoader.parse(ChildFirstClassLoader.java:34)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:545)
at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:438)
at org.javacs.ChildFirstClassLoader.parseClassPath(ChildFirstClassLoader.java:26)
at org.javacs.ChildFirstClassLoader.fromClassPath(ChildFirstClassLoader.java:40)
at org.javacs.LangTools.createLangToolsClassLoader(LangTools.java:25)
at org.javacs.Main.main(Main.java:72)
Caused by: java.net.MalformedURLException: no protocol: out/fat-jar.jar
at java.net.URL.<init>(URL.java:593)
at java.net.URL.<init>(URL.java:490)
at java.net.URL.<init>(URL.java:439)
at org.javacs.ChildFirstClassLoader.parse(ChildFirstClassLoader.java:32)
... 11 more
Anyone had any luck setting this up?
No, I ended up using IntelliJ IDEA. If you get it working, though, I'd like to know how!
I used #58 to make it work with vim-lsp - not sure how it's working compared to VSCode though
Hi, author of #58 here - I've had (my fork of) this up and running with LanguageClient-neovim for several months now and have been happily dogfooding it. I'm sure that there is probably _something I've missed and features I'm missing out on but its functional at least. So I can confirm that it's possible (at least on my machine) to get this up and running without vscode.
What I'd suggest (on mobile so can't test this) is using a full path $(pwd)/out/fat-jar.jar
. This is what my init.vim
and the typescript vscode extension do. I don't know much about java classpaths, but that seems to be the main difference between what works and what you are trying?
Let me know if this works for you in terms of getting it up and running, @Limeth @clintgibler. Also @georgewfraser I haven't got any feedback from the pull request yet - would you be able to give it a look?
It should work standalone now with https://github.com/georgewfraser/vscode-javac/commit/29504ed7bb0d5d370d1baaec479be76ad32b1816
Sorry for slowness @ashfordneil
If someone can write instructions for using this with VIM etc, I would happily accept a PR
I am getting this problem:
LanguageClient project root: /home/nemanjan00/RAF/mreze1-client
[Warning] Couldn't find com.sun.tools.javac.resources.compiler_en in child class loader
[Warning] Couldn't find com.sun.tools.javac.resources.compiler_en_US in child class loader
[Warning] Couldn't find com.sun.tools.javac.resources.javac_en in child class loader
[Warning] Couldn't find com.sun.tools.javac.resources.javac_en_US in child class loader
Note that I am using openjdk and not sun java...
@ashfordneil Thanks for the heads up. I haven't had time to test this but I'm glad people are working on it, much appreciated :)
@nemanjan00 Did you end up figuring out what the issue was? I'm experiencing the same.
Nope. Ended up using jdtls...
Also getting the same error re @nemanjan00
Could someone who is running this standalone with vim-lsp share an example of the setup steps? I mvn compile
then used the link script to jlink it together. When I set it up with vim-lsp I see NullReferenceExceptions.
These are the logs I am seeing when I open vim for this Java project, and then run the command :LspDefinition
to go to definition.
Sun 17 Feb 2019 05:49:17 AM UTC:[{"response":{"data":{"__data__":"vim-lsp","init_result":{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"referencesProvider":true,"codeLensProvider":{"resolveProvider":true},"hoverProvider":true,"documentFormattingProvider":true,"signatureHelpProvider":{"triggerCharacters":["(",","]},"foldingRangeProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"definitionProvider":true,"textDocumentSync":2,"completionProvider":{"resolveProvider":true,"triggerCharacters":["."]}}}},"server_name":"java-language-server"},"message":"lsp server already initialized"}}]
Sun 17 Feb 2019 05:49:17 AM UTC:[{"response":{"data":{"__data__":"vim-lsp","server_name":"java-language-server"},"message":"configuration sent"}}]
Sun 17 Feb 2019 05:49:17 AM UTC:[{"response":{"data":{"path":"file:///home/leighmcculloch/devel/java-language-server/src/main/java/org/javacs/Parser.java","__data__":"vim-lsp","server_name":"java-language-server"},"message":"already opened"}}]
Sun 17 Feb 2019 05:49:17 AM UTC:[{"response":{"data":{"path":"file:///home/leighmcculloch/devel/java-language-server/src/main/java/org/javacs/Parser.java","__data__":"vim-lsp","server_name":"java-language-server"},"message":"not dirty"}}]
Sun 17 Feb 2019 05:49:17 AM UTC:["--->",1,"java-language-server",{"method":"textDocument/definition","on_notification":"---funcref---","params":{"textDocument":{"uri":"file:///home/leighmcculloch/devel/java-language-server/src/main/java/org/javacs/Parser.java"},"position":{"character":29,"line":21}}}]
Sun 17 Feb 2019 05:49:17 AM UTC:["<---",1,"java-language-server",{"response":{"id":3,"jsonrpc":"2.0","result":{"code":-32603}},"request":{"method":"textDocument/definition","jsonrpc":"2.0","id":3,"params":{"textDocument":{"uri":"file:///home/leighmcculloch/devel/java-language-server/src/main/java/org/javacs/Parser.java"},"position":{"character":29,"line":21}}}}]
Sun 17 Feb 2019 05:49:17 AM UTC:["s:on_stdout client request on_notification() error","Vim(if):E716: Key not present in Dictionary: uri","function <SNR>102_out_cb[2]..<SNR>101_on_stdout[73]..<lambda>20[1]..<SNR>124_handle_location[10]..lsp#ui#vim#utils#locations_to_loc_list, line 12"]
Sun 17 Feb 2019 05:49:17 AM UTC:["<---(stderr)",1,"java-language-server",["05:49:17.926\tINFO\torg.javacs.JavaLanguageServer gotoDefinition\tGo-to-def at file:///home/leighmcculloch/devel/java-language-server/src/main/java/org/javacs/Parser.java:22...","05:49:17.928\tINFO\torg.javacs.JavaLanguageServer updateActiveFile\tRecompile active file...","05:49:17.929\tSEVERE\torg.javacs.lsp.LSP connect\tnull","java.lang.NullPointerException","\tat org.javacs.JavaLanguageServer.updateActiveFile(JavaLanguageServer.java:626)","\tat org.javacs.JavaLanguageServer.gotoDefinition(JavaLanguageServer.java:758)","\tat org.javacs.lsp.LSP.connect(LSP.java:352)","\tat org.javacs.Main.main(Main.java:25)","",""]]
@georgewfraser To help make using java-language-service in a standalone context simpler I opened #77 to re-add building a fat jar.
#80 contains instructions for how to use with VIM.
@leighmcculloch could you explain what this means:
This tool is not compatible with vim-lsp as it only supports LSPv2.0.
vim-lsp requires v3.0?
vim-lsp
only sends v2.0 messages from best I can tell. Notably it doesn't send the initialized
message to language servers, so language servers like this one that are coded to v3.0 may not work.