emacs-lsp / lsp-java

lsp-mode :heart: java
https://emacs-lsp.github.io/lsp-java
GNU General Public License v3.0
647 stars 90 forks source link

Are different JDK/JREs per project possible? #254

Closed TheBlob42 closed 4 years ago

TheBlob42 commented 4 years ago

Describe the bug I am currently having problems while working with projects which need a different Java versions for building.

I have installed OpenJDK 11 (my system default) and OpenJDK 8, my lsp-java version is 20200725.1809.

I have installed the Java language server and initialized the workspace with Java 11, which for the most of my projects works absolutely fine. Unfortunately I also have to work with projects that require Java 8 for building. When I add one of those to my workspace I see the following error message when I start the Java language server:

Jul 28, 2020, 4:39:53 PM Synchronize project "some-work-project" failed due to an error connecting to the Gradle build.
Could not create an instance of Tooling API implementation using the specified Gradle distribution '<link to our custom Artifactory>/gradle/gradle-4.6-bin.zip'.
org.gradle.tooling.GradleConnectionException: Could not create an instance of Tooling API implementation using the specified Gradle distribution '<link to our custom Artifactory>/gradle/gradle-4.6-bin.zip'.
    at org.gradle.tooling.internal.consumer.loader.DefaultToolingImplementationLoader.create(DefaultToolingImplementationLoader.java:99)
    at org.gradle.tooling.internal.consumer.loader.CachingToolingImplementationLoader.create(CachingToolingImplementationLoader.java:45)
    at org.gradle.tooling.internal.consumer.loader.SynchronizedToolingImplementationLoader.create(SynchronizedToolingImplementationLoader.java:44)
    at org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor.onStartAction(LazyConsumerActionExecutor.java:104)
    at org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor.run(LazyConsumerActionExecutor.java:86)
    at org.gradle.tooling.internal.consumer.connection.CancellableConsumerActionExecutor.run(CancellableConsumerActionExecutor.java:45)
    at org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor.run(ProgressLoggingConsumerActionExecutor.java:61)
    at org.gradle.tooling.internal.consumer.connection.RethrowingErrorsConsumerActionExecutor.run(RethrowingErrorsConsumerActionExecutor.java:38)
    at org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor.lambda$run$0(DefaultAsyncConsumerActionExecutor.java:55)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:834)
    at org.gradle.tooling.internal.consumer.BlockingResultHandler.getResult(BlockingResultHandler.java:46)
    at org.gradle.tooling.internal.consumer.DefaultModelBuilder.get(DefaultModelBuilder.java:51)
    at org.gradle.tooling.internal.consumer.DefaultProjectConnection.getModel(DefaultProjectConnection.java:50)
    at org.eclipse.buildship.core.internal.util.gradle.CompatProjectConnection.getModel(CompatProjectConnection.java:53)
    at org.eclipse.buildship.core.internal.util.gradle.IdeAttachedProjectConnection.configureOperation(IdeAttachedProjectConnection.java:68)
    at org.eclipse.buildship.core.internal.util.gradle.IdeAttachedProjectConnection.model(IdeAttachedProjectConnection.java:59)
    at org.eclipse.buildship.core.internal.util.gradle.IdeAttachedProjectConnection.getModel(IdeAttachedProjectConnection.java:86)
    at org.eclipse.buildship.core.internal.workspace.EclipseModelUtils.runTasksAndQueryModels(EclipseModelUtils.java:56)
    at org.eclipse.buildship.core.internal.workspace.DefaultModelProvider.lambda$null$4(DefaultModelProvider.java:75)
    at org.eclipse.buildship.core.internal.DefaultGradleBuild$GradleConnectionOperation.runInToolingApi(DefaultGradleBuild.java:329)
    at org.eclipse.buildship.core.internal.operation.DefaultToolingApiOperationManager$WorkspaceRunnableAdapter.run(DefaultToolingApiOperationManager.java:58)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2292)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2317)
    at org.eclipse.buildship.core.internal.operation.DefaultToolingApiOperationManager.run(DefaultToolingApiOperationManager.java:39)
    at org.eclipse.buildship.core.internal.DefaultGradleBuild.withConnection(DefaultGradleBuild.java:122)
    at org.eclipse.buildship.core.internal.workspace.DefaultModelProvider.lambda$fetchEclipseProjectAndRunSyncTasks$5(DefaultModelProvider.java:75)
    at com.google.common.cache.LocalCache$LocalManualCache$1.load(LocalCache.java:4878)
    at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3529)
    at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2278)
    at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2155)
    at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2045)
    at com.google.common.cache.LocalCache.get(LocalCache.java:3953)
    at com.google.common.cache.LocalCache$LocalManualCache.get(LocalCache.java:4873)
    at org.eclipse.buildship.core.internal.workspace.DefaultModelProvider.getFromCache(DefaultModelProvider.java:98)
    at org.eclipse.buildship.core.internal.workspace.DefaultModelProvider.executeOperation(DefaultModelProvider.java:90)
    at org.eclipse.buildship.core.internal.workspace.DefaultModelProvider.fetchEclipseProjectAndRunSyncTasks(DefaultModelProvider.java:72)
    at org.eclipse.buildship.core.internal.DefaultGradleBuild$SynchronizeOperation.runInToolingApi(DefaultGradleBuild.java:226)
    at org.eclipse.buildship.core.internal.operation.DefaultToolingApiOperationManager$WorkspaceRunnableAdapter.run(DefaultToolingApiOperationManager.java:58)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2292)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2317)
    at org.eclipse.buildship.core.internal.operation.DefaultToolingApiOperationManager.run(DefaultToolingApiOperationManager.java:39)
    at org.eclipse.buildship.core.internal.DefaultGradleBuild$SynchronizeOperation.run(DefaultGradleBuild.java:192)
    at org.eclipse.buildship.core.internal.DefaultGradleBuild.synchronize(DefaultGradleBuild.java:100)
    at org.eclipse.buildship.core.internal.workspace.SynchronizationJob.runInToolingApi(SynchronizationJob.java:64)
    at org.eclipse.buildship.core.internal.workspace.SynchronizationJob.runInToolingApi(SynchronizationJob.java:30)
    at org.eclipse.buildship.core.internal.operation.ToolingApiJob$1.runInToolingApi(ToolingApiJob.java:54)
    at org.eclipse.buildship.core.internal.operation.DefaultToolingApiOperationManager$WorkspaceRunnableAdapter.run(DefaultToolingApiOperationManager.java:58)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2292)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2317)
    at org.eclipse.buildship.core.internal.operation.DefaultToolingApiOperationManager.run(DefaultToolingApiOperationManager.java:39)
    at org.eclipse.buildship.core.internal.operation.ToolingApiJob.run(ToolingApiJob.java:65)
    at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
Caused by: org.gradle.internal.service.ServiceCreationException: Could not create service of type FileMetadataAccessor using NativeServices.createFileMetadataAccessor().
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:857)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:808)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider.getInstance(DefaultServiceRegistry.java:612)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:669)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:821)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:807)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider.getInstance(DefaultServiceRegistry.java:612)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:669)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:821)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:807)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider.getInstance(DefaultServiceRegistry.java:612)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:669)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:821)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:807)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider.getInstance(DefaultServiceRegistry.java:612)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:669)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:821)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:807)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider.getInstance(DefaultServiceRegistry.java:612)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:669)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:821)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:807)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider.getInstance(DefaultServiceRegistry.java:612)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:669)
    at org.gradle.internal.service.DefaultServiceRegistry.get(DefaultServiceRegistry.java:322)
    at org.gradle.internal.service.DefaultServiceRegistry.get(DefaultServiceRegistry.java:316)
    at org.gradle.tooling.internal.provider.DefaultConnection.initializeServices(DefaultConnection.java:125)
    at org.gradle.tooling.internal.provider.DefaultConnection.configure(DefaultConnection.java:102)
    at org.gradle.tooling.internal.consumer.connection.AbstractPost12ConsumerConnection.configure(AbstractPost12ConsumerConnection.java:37)
    at org.gradle.tooling.internal.consumer.loader.DefaultToolingImplementationLoader.createConnection(DefaultToolingImplementationLoader.java:104)
    at org.gradle.tooling.internal.consumer.loader.DefaultToolingImplementationLoader.create(DefaultToolingImplementationLoader.java:90)
    at org.gradle.tooling.internal.consumer.loader.CachingToolingImplementationLoader.create(CachingToolingImplementationLoader.java:45)
    at org.gradle.tooling.internal.consumer.loader.SynchronizedToolingImplementationLoader.create(SynchronizedToolingImplementationLoader.java:44)
    at org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor.onStartAction(LazyConsumerActionExecutor.java:104)
    at org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor.run(LazyConsumerActionExecutor.java:86)
    at org.gradle.tooling.internal.consumer.connection.CancellableConsumerActionExecutor.run(CancellableConsumerActionExecutor.java:45)
    at org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor.run(ProgressLoggingConsumerActionExecutor.java:61)
    at org.gradle.tooling.internal.consumer.connection.RethrowingErrorsConsumerActionExecutor.run(RethrowingErrorsConsumerActionExecutor.java:38)
    at org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor.lambda$run$0(DefaultAsyncConsumerActionExecutor.java:55)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalArgumentException: Could not determine java version from '11.0.7'.
    at org.gradle.api.JavaVersion.toVersion(JavaVersion.java:72)
    at org.gradle.api.JavaVersion.current(JavaVersion.java:82)
    at org.gradle.internal.nativeintegration.services.NativeServices.createFileMetadataAccessor(NativeServices.java:253)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.internal.service.ReflectionBasedServiceMethod.invoke(ReflectionBasedServiceMethod.java:35)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:855)
    ... 44 more

This makes sense to me as the language server uses Java 11 while the project specific Gradle-Wrapper version only works with Java 8. I am then stuck with a project which can not resolve its dependencies. I can not build it via lsp-java-build-project, jump to definitions via lsp-find-definition or update the project configuration via lsp-java-update-project-configuration.

However when using the Eclipse IDE (on which the language server is based on, if I'm correct) this works fine, because the Java 8 JRE system library was added to the build path. This can also be seen in the corresponding .classpath file of the project:

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" output="bin/main" path="src/main/java">
        <attributes>
            <attribute name="gradle_scope" value="main"/>
            <attribute name="gradle_used_by_scope" value="main,test"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="src" output="bin/main" path="src/main/resources">
        <attributes>
            <attribute name="gradle_scope" value="main"/>
            <attribute name="gradle_used_by_scope" value="main,test"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="src" output="bin/test" path="src/test/java">
        <attributes>
            <attribute name="gradle_scope" value="test"/>
            <attribute name="gradle_used_by_scope" value="test"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="src" output="bin/test" path="src/test/resources">
        <attributes>
            <attribute name="gradle_scope" value="test"/>
            <attribute name="gradle_used_by_scope" value="test"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
    <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
    <classpathentry kind="output" path="bin/default"/>
</classpath>

Now I have searched quite a bit on how to achieve the same configuration for lsp-java but could not find any explanation or solution :crying_cat_face:

Is it currently possible to use a different Java JDK/JRE per project like in the Eclipse IDE and get rid of my errors?

yyoncho commented 4 years ago

It seems like you can using this https://github.com/redhat-developer/vscode-java/#setting-the-jdk which translates to

(setq lsp-java-configuration-runtimes '[(:name "JavaSE-8"
                                                 :path "/home/kyoncho/.java-jdk/"
                                                 :default t)])

I will close the issue, but you can use it for further questions.

TheBlob42 commented 4 years ago

@yyoncho I was not aware of this option, thank you for pointing it out :+1:

Unfortunately I still did not get it to work as I get an error message which I do not understand how to handle.

I have added the following to my lsp-java config:

(setq lsp-java-configuration-runtimes '[(:name "Open JDK 11" 
                                         :path "/usr/lib/jvm/java-11-openjdk-amd64/"
                                         :default t)
                        (:name "JavaSE-8"
                         :path "/usr/lib/jvm/java-8-openjdk-amd64/")])

But when I start the Java language server I see the following error message in the *lsp-log* buffer:

Jul 30, 2020, 8:43:53 AM Runtime at '/usr/lib/jvm/java-11-openjdk-amd64/' is not compatible with the 'Open JDK 11' environment
Jul 30, 2020, 8:43:53 AM Runtime at '/usr/lib/jvm/java-8-openjdk-amd64/' is not compatible with the 'JavaSE-8' environment

I am not sure what this means and what to change :thinking:

Maybe you can clarify this for me :pray:

yyoncho commented 4 years ago

I don't know - I have to read the jdtls code to get that for you which I generally can do but I don't have the time. I would recommend you to ask in the server repo.

TheBlob42 commented 4 years ago

No problem, I don't expect you to read it for me :smile: I just thought you might know what is going on.

I will check out the jdtls repo and see what I can find out. If I find a solution, I will post it here.

TheBlob42 commented 4 years ago

I haven't figured it out completely, but I will document my progress here for others which might face the same problem:

From this issue in the jdtls repository I found out that

Runtime name must be one of:

"J2SE-1.5", "JavaSE-1.6", "JavaSE-1.7", "JavaSE-1.8", "JavaSE-9", "JavaSE-10", "JavaSE-11", "JavaSE-12", "JavaSE-13"

List will be updated with each supported release of the JDK

This resolves my errors Runtime at ...' is not compatible with the '...' environment (see my earlier comment)

Unfortunately for the project synchronization it still uses the wrong Java version and the original error remains :crying_cat_face:

yyoncho commented 4 years ago

maybe try to delete .project file?

yyoncho commented 4 years ago

... and classpath and reimport the project.

TheBlob42 commented 4 years ago

I tried both, but without success :disappointed:

I also found this issue regarding VS Code, but even with those changes I could not make work. The jdtls always uses Java 11 for the lsp-java-update-project-configuration command (which then fails as above). Therefore I am still investigating :thinking:

deb75 commented 4 years ago

It seems to work, I have the LSP server running with java 11 and compiling with java 8. See my similar issue #249.

TheBlob42 commented 4 years ago

From my testing so far it seems to be more of a Gradle/buildship problem :thinking:

The compilation and everything related actually works fine by using lsp-java-configuration-runtimes (similiar to how you did it in your ticket @deb75). I can jump to methods, rename variables/methods, look at Javadoc etc. The remaining problem I have is that the dependencies via Gradle are not loaded/synchronized, so I can not access any classes from outside my project.

From this issue I can currently only assume that Gradle buildship is always started with the Java version which is used for starting the jdtls (in my case "Java 11"), which is incompatible with the Gradle wrapper of my project (which needs "Java 8") and then leads to these Caused by: java.lang.IllegalArgumentException: Could not determine java version from '11.0.7'. exceptions. On the other hand I can't start the jdtls with Java 8 as the newest version does not support it anymore :crying_cat_face:

I haven't found any workaround for this issue yet.

Did you have similar issues with your build tools @deb75?

yyoncho commented 4 years ago

I haven't found any workaround for this issue yet.

FWIW you may use older version.

For the other issue, you may ask in server repo...

TheBlob42 commented 4 years ago

Yes of course this is a workaround :smile:

I will still check the server repo for an actual solution but otherwise this is done for me. Thank you for all the help :+1:

yyoncho commented 4 years ago

I think that there is nothing to do here on lsp-java side. Please reopen if you disagree.

CsBigDataHub commented 3 years ago

@yyoncho @TheBlob42

Looks like VS code tackled this with https://devblogs.microsoft.com/java/java-on-visual-studio-code-update-november-2020/#java-runtime-configuration-wizard

Can we do that same using .dir_locals.el ? So that we can specify different runtimes for different projects. I have been working on Java 8 and Java 11 projects in parallel and trying to a find an elegant way to handle this in emacs.

Thanks @CsBigDataHub

yyoncho commented 3 years ago

@CsBigDataHub this looks like something that we want to have too.

CsBigDataHub commented 3 years ago

@CsBigDataHub this looks like something that we want to have too.

Thanks @yyoncho should I create an issue to track the feature?

renatoathaydes commented 3 years ago

With Java using a 6-month release cadence, it's impossible to use emacs to code in Java without being able to tell emacs the JDK to use per-project. I have some projects where I don't use Maven nor Gradle, and would be nice to have somewhere to tell emacs "please use this JDK on this git project", which could perhaps be done by integrating with projectile?

In IntelliJ, this is trivial to do, it even auto-discovers JDKs installed on the system and has a button to auto-download a particular version. It also lets us choose the API version we want to code against, which means we can use IDE checks for, say, Java 8 (useful for libraries as we may want to keep compatibility for a long time), while still actually compiling locally with Java 17.

In emacs, it would be easy to do that by integrating with SDKMAN! in case it's installed.