Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Package name not recognized when opening standalone java files #1766

Closed
wants to merge 1 commit into from

Conversation

snjeza
Copy link
Contributor

@snjeza snjeza commented May 10, 2021

Fixes #1764

Signed-off-by: Snjezana Peco [email protected]

Copy link
Contributor

@rgrunber rgrunber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, change looks good. It looks like this adjusts the root path correctly now prior to passing it to loading the invisible project.

@snjeza
Copy link
Contributor Author

snjeza commented May 11, 2021

@rgrunber @jdneo I have updated the PR.

Comment on lines +55 to +66
while (!BaseInitHandler.isWorkspaceInitialized()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore and keep waiting
}
}
try {
Job.getJobManager().join(SyntaxInitHandler.JAVA_LS_INITIALIZATION_JOBS, null);
} catch (OperationCanceledException | InterruptedException e) {
logException(e.getMessage(), e);
}
Copy link
Contributor

@rgrunber rgrunber May 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this support the usage of triggerFiles or is this some separate issue discovered ? I think we should avoid having requests from the client waiting on each other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes VS Code calls BuildWorkspaceHandler.buildWorkspace(boolean, IProgressMonitor) before initializing a workspace.
@rgrunber you have try the following:

  • clean the Java LS workspace
  • create a vscode-java vsix with and without this code
  • install the vsix
  • unzip wrong-packagename
cd wrong-packagename
code .

https://github.com/snjeza/vscode-test/raw/master/java-0.79.3.vsix includes this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdneo
Copy link
Contributor

jdneo commented May 12, 2021

Tried to build the jar and played for a while. Encountered some problems:

  1. I got an error
c:\Users\sheche\AppData\Roaming\Code\User\workspaceStorage\b3281020046c073d2668dc7168c1ed4f\redhat.java\jdt_ws: Error: EBUSY: resource busy or locked, unlink 'c:\Users\sheche\AppData\Roaming\Code\User\workspaceStorage\b3281020046c073d2668dc7168c1ed4f\redhat.java\jdt_ws\.metadata\.plugins\org.eclipse.m2e.logback.configuration\0.log'

When I tried to clean the workspace and restart VS Code. (might be a different issue)

  1. There are still some possibility seeing The declared package "mypackage" does not match the expected package "" if keeping clean and restart for several times.

To repro this, you can first set the setting

"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005,quiet=y" 

to the project to open the debug port, and then attach Java Debugger to that project.

Meanwhile, set a breakpoint at InvisibleProjectImporter.java@line 339:

if (project == null || !project.isAccessible()) {
    return "";  // Set breakpoint here
}

And then keep doing clean and restart. You may hit the breakpoint after some tries.

Since the package name is returned as an empty string, thus the result to infer the source directory is wrong because of the empty package name.

It seems that when we clean the workspace, it will also remove the default project, which will make the code fall into the if block that returns an empty package name

@jdneo
Copy link
Contributor

jdneo commented May 12, 2021

After some deep investigation, there are some more findings.

The problem can be divided into the following small pieces.

The declared package "mypackage" does not match the expected package ""

The repro steps is what I mentioned above, I guess it's caused by removing the default project when clean the workspace storage.

The file is not on source path after reload.

This is caused by here:

String invisibleProjectOutputPath = getString(configuration, JAVA_PROJECT_OUTPUT_PATH_KEY, "");

The output path is set to empty string if it's not in the configuration body sent from the client side. But it's initial value is null at first. This difference will invoke preferencesChange() in InvisibleProjectPreferenceChangeListener.

Changing the default value to null should fix this one:

String invisibleProjectOutputPath = getString(configuration, JAVA_PROJECT_OUTPUT_PATH_KEY, null);

About persist the triggerFiles

Suppose a user has been set the source path setting before, and then he remove that entry in his settings.json. In this case, preferencesChange() in InvisibleProjectPreferenceChangeListener will be invoked with the new sourcePath equals null.

In that case we originally think the user should be aware of that he can update the source path and the remove action indicates something as no source paths. However, If we persist the triggerFiles, then we can re-guess the source path as we first import the invisible project, which I think is a better approach.

@snjeza
Copy link
Contributor Author

snjeza commented May 12, 2021

When I tried to clean the workspace and restart VS Code. (might be a different issue)

Related issue - #1010

The repro steps is what I mentioned above, I guess it's caused by removing the default project when clean the workspace storage.

@jdneo Could you reproduce the issue with https://github.com/snjeza/vscode-test/raw/master/java-0.79.3.vsix

@rgrunber
Copy link
Contributor

rgrunber commented May 12, 2021

Ok so (1) has a separate bug if we wish to address it, which just leaves (2) and my complaint about having BuildWorkspaceHandler.buildWorkspace(..) wait on initialization.

With the change by @snjeza (whether I use java-0.79.3.vsix or build the latest PR) , I still see the ".. package does not match expected package .." after a few clean/restarts. If I set the invisibleProjectOutputPath initialization in Preferences so it defaults to null, I still see the same issue :|

I also see this in the logs when the diagnostic is emitted :

[Error - 5:13:17 PM] May 12, 2021, 5:13:16 PM Failed to build workspace.
jdt.ls-java-project does not exist
Java Model Exception: Java Model Status [jdt.ls-java-project does not exist]
	at org.eclipse.jdt.internal.core.JavaElement.newJavaModelException(JavaElement.java:583)
	at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:254)
	at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:596)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:326)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:312)
	at org.eclipse.jdt.internal.core.JavaElement.getChildren(JavaElement.java:267)
	at org.eclipse.jdt.internal.core.JavaProject.getPackageFragmentRoots(JavaProject.java:2322)
	at org.eclipse.jdt.internal.core.JavaProject.getPackageFragments(JavaProject.java:2346)
	at org.eclipse.jdt.ls.core.internal.managers.ProjectsManager.cleanupResources(ProjectsManager.java:212)
	at org.eclipse.jdt.ls.core.internal.handlers.BuildWorkspaceHandler.buildWorkspace(BuildWorkspaceHandler.java:67)
	at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$26(JDTLanguageServer.java:834)
	at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$51(JDTLanguageServer.java:1006)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:642)
	at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:295)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

For testing I was using a simple Java file with package yourpackage.mypackage; located under src/yourpackage/mypackage from the folder being imported.

One thing I noticed, is if the error happens after a "Clean Java Language Server Workspace", then if I just "Reload Window", it will load things correctly without errors. Monitoring the contents of .classpath after a workspace clean, I see that it first sets the "src" entry to <classpathentry kind="src" path="_/src/mypackage/yourpackage"/> before it gets changed to <classpathentry kind="src" path="_/src"/>, but the error still shows up. To me this seems to imply validation is done on the previous (invalid source entry). This probably explains why a simple restart fixes things.

@snjeza
Copy link
Contributor Author

snjeza commented May 13, 2021

@jdneo
Copy link
Contributor

jdneo commented May 13, 2021

0.79.4 works well on my windows machine. Also share some findings from my side.

Seems the root cause is that when we handle the initialized notification from client side:

try {
    Job.getJobManager().join(InitHandler.JAVA_LS_INITIALIZATION_JOBS, null);
} catch (OperationCanceledException | InterruptedException e) {
    logException(e.getMessage(), e);
}

we use the above code to wait the initialize job finished. Unfortunately, it does not take effect for some reason. (I suspect it's because a new scheduled job is not set to running status immediately?). Thus, we failed to block the dispatch thread here, then the server starts to handle with the following client request even it's actually not ready.

Changing to JobHelpers.waitForInitializeJobs(); works on my side. In waitForInitializeJobs() it directly get the initialization jobs no matter what status it is, I guess that's why it works.

// It is very interesting since it seems that this issue is there for a long time. 🤔

Anyway, I also upload a fix: #1767. @rgrunber @snjeza Feel free to take a look. And you can just close it if @snjeza has already done the job.

@@ -68,6 +70,7 @@
private ListenerList<IPreferencesChangeListener> preferencesChangeListeners;
private IEclipsePreferences eclipsePrefs;
private static Map<String, Template> templates = new LinkedHashMap<>();
private static Collection<IPath> triggerFiles;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not persist the trigger files. They should be used only once. And only use them to calculate a default source path when initializing a new invisible project.

If the user modifies the source paths in a subsequent action, such as changing the workspace setting java.project.sourcePaths, we should always follow the user's action to update the source paths.

This means that if the user sets sourcePaths to null, we should just clean up all the source paths. There is no need to fall back and use trigger files to calculate the source paths again. Since the user knows the settings to change the source paths, he/she can change them back if he/she wants.

IPath outputPath = InvisibleProjectImporter.getOutputPath(javaProject, newPreferences.getInvisibleProjectOutputPath(), true /*isUpdate*/);
IClasspathEntry[] classpathEntries = InvisibleProjectImporter.resolveClassPathEntries(javaProject, sourcePaths, excludingPaths, outputPath);
javaProject.setRawClasspath(classpathEntries, outputPath, new NullProgressMonitor());
InvisibleProjectImporter.updateSourcePaths(javaProject);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always use the trigger files to update source paths, regardless of the sourcePaths preference. It's a breaking change.

Only if sourcePaths preference is explicitly changed, then need to update source paths. Otherwise, just update project output path directly via javaProject.setOutputLocation.

See @jdneo's PR https://github.com/eclipse/eclipse.jdt.ls/pull/1767/files

@snjeza
Copy link
Contributor Author

snjeza commented May 13, 2021

See #1767

@snjeza snjeza closed this May 13, 2021
@rgrunber
Copy link
Contributor

Thanks, we can have a look there. FWIW, your change (java-0.79.4.vsix ) does seem to work for me now and from what I can tell, modifying java.project.sourcePaths is respected, but if there's a solution that avoid touching triggerFiles / waiting on LSP calls, we should go with that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Package name not recognized when opening standalone java files
4 participants