java-language-server icon indicating copy to clipboard operation
java-language-server copied to clipboard

Standalone Version

Open meunierd opened this issue 7 years ago • 34 comments

Would it be possible to separate out just the langserver so that it can be used with other clients?

meunierd avatar Jul 11 '17 18:07 meunierd

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.

georgewfraser avatar Jul 15 '17 20:07 georgewfraser

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

meunierd avatar Jul 15 '17 21:07 meunierd

Very odd. Just re-cloned and ran mvn test and it worked. Try again on latest master?

georgewfraser avatar Jul 22 '17 00:07 georgewfraser

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

meunierd avatar Jul 22 '17 01:07 meunierd

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 😅

georgewfraser avatar Jul 23 '17 00:07 georgewfraser

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

🤷‍♂️

meunierd avatar Jul 23 '17 01:07 meunierd

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.

georgewfraser avatar Jul 23 '17 04:07 georgewfraser

That worked!

meunierd avatar Jul 23 '17 17:07 meunierd

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 avatar Dec 09 '17 20:12 clintgibler

@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(...) }

georgewfraser avatar Dec 11 '17 19:12 georgewfraser

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?

clintgibler avatar Dec 12 '17 04:12 clintgibler

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

georgewfraser avatar Dec 14 '17 19:12 georgewfraser

OK cool, thanks!

clintgibler avatar Dec 18 '17 00:12 clintgibler

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

Limeth avatar Mar 08 '18 11:03 Limeth

Anyone had any luck setting this up?

nemanjan00 avatar Mar 31 '18 14:03 nemanjan00

No, I ended up using IntelliJ IDEA. If you get it working, though, I'd like to know how!

Limeth avatar Mar 31 '18 16:03 Limeth

I used #58 to make it work with vim-lsp - not sure how it's working compared to VSCode though

DonnieWest avatar Mar 31 '18 18:03 DonnieWest

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?

ashfordneil avatar Apr 13 '18 13:04 ashfordneil

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

georgewfraser avatar Apr 15 '18 01:04 georgewfraser

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...

nemanjan00 avatar Apr 24 '18 13:04 nemanjan00

@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 :)

clintgibler avatar Apr 29 '18 23:04 clintgibler

@nemanjan00 Did you end up figuring out what the issue was? I'm experiencing the same.

pungaliy avatar May 30 '18 18:05 pungaliy

Nope. Ended up using jdtls...

nemanjan00 avatar May 30 '18 18:05 nemanjan00

Also getting the same error re @nemanjan00

perks avatar Sep 06 '18 14:09 perks

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.

leighmcculloch avatar Feb 16 '19 22:02 leighmcculloch

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)","",""]]

leighmcculloch avatar Feb 17 '19 05:02 leighmcculloch

@georgewfraser To help make using java-language-service in a standalone context simpler I opened #77 to re-add building a fat jar.

leighmcculloch avatar Feb 17 '19 06:02 leighmcculloch

#80 contains instructions for how to use with VIM.

leighmcculloch avatar Feb 18 '19 00:02 leighmcculloch

@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?

laktak avatar Feb 19 '19 23:02 laktak

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.

leighmcculloch avatar Feb 20 '19 07:02 leighmcculloch