From cfb076c10a86523d734942f29364d0d90aa6634f Mon Sep 17 00:00:00 2001 From: Snjezana Peco Date: Mon, 6 May 2024 02:31:12 +0200 Subject: [PATCH] VSCode JLS error when loading project with lot of Maven sub modules Signed-off-by: Snjezana Peco --- .../core/internal/handlers/InitHandler.java | 4 +- .../internal/handlers/JDTLanguageServer.java | 9 +- .../handlers/WorkspaceEventsHandler.java | 4 + .../internal/managers/IProjectsManager.java | 26 ++++++ .../managers/MavenProjectImporter.java | 14 +-- .../managers/StandardProjectsManager.java | 50 ++++++++++- .../preferences/PreferenceManager.java | 8 +- .../StandardPreferenceManager.java | 85 ++++++++++--------- .../internal/handlers/InitHandlerTest.java | 2 + 9 files changed, 149 insertions(+), 53 deletions(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java index 3978a35056..27ea496fb4 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java @@ -255,7 +255,9 @@ public void triggerInitialization(Collection roots) { Job.getJobManager().wakeUp(ResourcesPlugin.FAMILY_AUTO_BUILD); IWorkspace workspace = ResourcesPlugin.getWorkspace(); if (workspace instanceof Workspace workspaceImpl) { - workspaceImpl.getBuildManager().waitForAutoBuildOff(); + if (!Job.getJobManager().isSuspended()) { + workspaceImpl.getBuildManager().waitForAutoBuildOff(); + } } Job job = new WorkspaceJob("Initialize Workspace") { @Override diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java index f75ebe61e9..82425dd448 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java @@ -309,8 +309,6 @@ public IStatus run(IProgressMonitor monitor) { classpathUpdateHandler.addElementChangeListener(); SourceAttachUpdateHandler attachListener = new SourceAttachUpdateHandler(client); attachListener.addElementChangeListener(); - pm.registerWatchers(); - debugTrace(">> watchers registered"); registerCapabilities(); // we do not have the user setting initialized yet at this point but we should @@ -328,10 +326,15 @@ public IStatus run(IProgressMonitor monitor) { IndexUtils.copyIndexesToSharedLocation(); JobHelpers.waitForBuildJobs(60 * 60 * 1000); // 1 hour logInfo(">> build jobs finished"); + // https://github.com/redhat-developer/vscode-java/issues/3637 - delay registerWatchers + pm.registerWatchers(); + debugTrace(">> watchers registered"); + pm.projectsBuildFinished(monitor); telemetryManager.onBuildFinished(System.currentTimeMillis()); workspaceDiagnosticsHandler.publishDiagnostics(monitor); } catch (OperationCanceledException | CoreException e) { logException(e.getMessage(), e); + pm.projectsBuildFinished(monitor); return Status.CANCEL_STATUS; } return Status.OK_STATUS; @@ -577,7 +580,7 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { try { boolean isAutobuildEnabled = preferenceManager.getPreferences().isAutobuildEnabled(); boolean autoBuildChanged = ProjectsManager.setAutoBuilding(isAutobuildEnabled); - if (jvmChanged || nullAnalysisOptionsUpdated && isAutobuildEnabled) { + if ((jvmChanged || nullAnalysisOptionsUpdated) && isAutobuildEnabled) { buildWorkspace(Either.forLeft(true)); } else if (autoBuildChanged && isAutobuildEnabled) { buildWorkspace(Either.forLeft(false)); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java index a893854f1f..e315a872d9 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java @@ -57,6 +57,10 @@ public WorkspaceEventsHandler(ProjectsManager projects, JavaClientConnection con Thread eventThread = new Thread(() -> { while(true) { try { + // https://github.com/redhat-developer/vscode-java/issues/3637 + while (!pm.isBuildFinished()) { + Thread.sleep(200); + } FileEvent event = queue.take(); handleFileEvent(event); } catch (InterruptedException e) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/IProjectsManager.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/IProjectsManager.java index aee9d55fe7..f84fc187ea 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/IProjectsManager.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/IProjectsManager.java @@ -103,4 +103,30 @@ default void unregisterListeners() { default void projectsImported(IProgressMonitor monitor) { // do nothing } + + /** + * Executed after the projects are imported and finished. + */ + default void projectsBuildFinished(IProgressMonitor monitor) { + // do nothing + } + + /** + * Check whether the build is finished + */ + default boolean isBuildFinished() { + return true; + }; + + default boolean shouldUpdateProjects() { + return false; + } + + default void setShouldUpdateProjects(boolean update) { + // do nothing + } + + default void updateMavenProjects(IProgressMonitor monitor) { + // do nothing + }; } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java index c0492b12f9..c21213efe9 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java @@ -28,6 +28,7 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; @@ -288,15 +289,18 @@ private void updateProjects(Collection projects, long lastWorkspaceSta while (iterator.hasNext()) { IProject project = iterator.next(); project.open(monitor); + IFile pomFile = project.getFile(POM_FILE); + pomFile.refreshLocal(IResource.DEPTH_ZERO, monitor); + if (!needsMavenUpdate(pomFile, lastWorkspaceStateSaved)) { + iterator.remove(); + continue; + } if (Platform.OS_WIN32.equals(Platform.getOS())) { project.refreshLocal(IResource.DEPTH_ONE, monitor); ((Workspace) ResourcesPlugin.getWorkspace()).getRefreshManager().refresh(project); } else { project.refreshLocal(IResource.DEPTH_INFINITE, monitor); } - if (!needsMavenUpdate(project, lastWorkspaceStateSaved)) { - iterator.remove(); - } } if (projects.isEmpty()) { return; @@ -318,8 +322,8 @@ public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { }.schedule(); } - private boolean needsMavenUpdate(IProject project, long lastWorkspaceStateSaved) { - return project.getFile(POM_FILE).getLocalTimeStamp() > lastWorkspaceStateSaved; + private boolean needsMavenUpdate(IResource pomFile, long lastWorkspaceStateSaved) { + return pomFile.getLocalTimeStamp() > lastWorkspaceStateSaved; } private Set getMavenProjects(File directory, MavenModelManager modelManager, IProgressMonitor monitor) throws OperationCanceledException { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/StandardProjectsManager.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/StandardProjectsManager.java index 95c0cb94d2..4f6420d4f1 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/StandardProjectsManager.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/StandardProjectsManager.java @@ -100,6 +100,18 @@ public class StandardProjectsManager extends ProjectsManager { protected static final String BUILD_SUPPORT_EXTENSION_POINT_ID = "buildSupport"; private static final Set watchers = new LinkedHashSet<>(); private PreferenceManager preferenceManager; + private boolean buildFinished; + private boolean shouldUpdateProjects; + + public boolean isShouldUpdateProjects() { + return shouldUpdateProjects; + } + + @Override + public void setShouldUpdateProjects(boolean shouldUpdateProjects) { + this.shouldUpdateProjects = shouldUpdateProjects; + } + //@formatter:off private static final List basicWatchers = Arrays.asList( "**/*.java", @@ -301,6 +313,10 @@ public static void configureSettings(Preferences preferences) { public static void configureSettings(Preferences preferences, boolean cleanWorkspace) { URI settingsUri = preferences.getSettingsAsURI(); + URI formatterUri = preferences.getFormatterAsURI(); + if (settingsUri == null && formatterUri == null && !cleanWorkspace) { + return; + } Properties properties = null; if (settingsUri != null) { try (InputStream inputStream = settingsUri.toURL().openStream()) { @@ -312,7 +328,6 @@ public static void configureSettings(Preferences preferences, boolean cleanWorks } } initializeDefaultOptions(preferences); - URI formatterUri = preferences.getFormatterAsURI(); Map formatterOptions = null; if (formatterUri != null) { try (InputStream inputStream = formatterUri.toURL().openStream()) { @@ -344,7 +359,9 @@ public static void configureSettings(Preferences preferences, boolean cleanWorks } }); } - JavaCore.setOptions(javaOptions); + if (!Objects.equals(javaOptions, JavaCore.getOptions())) { + JavaCore.setOptions(javaOptions); + } if (cleanWorkspace && preferences.isAutobuildEnabled()) { new WorkspaceJob("Clean workspace...") { @@ -382,6 +399,7 @@ public Optional getBuildSupport(IProject project) { return buildSupports().filter(bs -> bs.applies(project)).findFirst(); } + @Override protected Stream buildSupports() { Map supporters = new TreeMap<>(); IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(IConstants.PLUGIN_ID, BUILD_SUPPORT_EXTENSION_POINT_ID); @@ -648,4 +666,32 @@ public void projectsImported(IProgressMonitor monitor) { androidSupport.onDidProjectsImported(monitor); this.preferenceManager.getPreferences().updateAnnotationNullAnalysisOptions(); } + + @Override + public void projectsBuildFinished(IProgressMonitor monitor) { + this.buildFinished = true; + if (this.shouldUpdateProjects) { + updateMavenProjects(monitor); + this.shouldUpdateProjects = false; + } + } + + @Override + public void updateMavenProjects(IProgressMonitor monitor) { + int threads = 0; + for (IProject project : ProjectUtils.getAllProjects()) { + if (ProjectUtils.isMavenProject(project)) { + updateProject(project, true); + if (threads++ > 3) { + JobHelpers.waitForJobs(IConstants.UPDATE_PROJECT_FAMILY, monitor); + threads = 0; + } + } + } + } + + @Override + public boolean isBuildFinished() { + return buildFinished; + } } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/PreferenceManager.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/PreferenceManager.java index 227cf8fe8a..c7de146f3d 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/PreferenceManager.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/PreferenceManager.java @@ -164,7 +164,9 @@ public static void initializeJavaCoreOptions() { // workaround for https://github.com/redhat-developer/vscode-java/issues/718 javaCoreOptions.put(JavaCore.CORE_CIRCULAR_CLASSPATH, JavaCore.WARNING); javaCoreOptions.put(JavaCore.COMPILER_IGNORE_UNNAMED_MODULE_FOR_SPLIT_PACKAGE, JavaCore.ENABLED); - JavaCore.setOptions(javaCoreOptions); + if (!Objects.equals(javaCoreOptions, JavaCore.getOptions())) { + JavaCore.setOptions(javaCoreOptions); + } } private static void reloadTemplateStore() { @@ -226,7 +228,9 @@ public void update(Preferences preferences) { } Hashtable options = JavaCore.getOptions(); preferences.updateTabSizeInsertSpaces(options); - JavaCore.setOptions(options); + if (!Objects.equals(options, JavaCore.getOptions())) { + JavaCore.setOptions(options); + } List resourceFilters = preferences.getResourceFilters(); IEclipsePreferences eclipsePreferences = InstanceScope.INSTANCE.getNode(IConstants.PLUGIN_ID); // add the resourceFilters preference; the org.eclipse.jdt.ls.filesystem plugin uses it diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/StandardPreferenceManager.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/StandardPreferenceManager.java index d518c45a0f..6e0a0275d0 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/StandardPreferenceManager.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/StandardPreferenceManager.java @@ -14,7 +14,6 @@ import java.io.File; import java.io.IOException; -import java.util.List; import java.util.Objects; import org.apache.maven.settings.Activation; @@ -30,8 +29,10 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.ls.core.internal.IConstants; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.eclipse.m2e.apt.MavenJdtAptPlugin; import org.eclipse.m2e.apt.preferences.PreferencesConstants; import org.eclipse.m2e.core.MavenPlugin; @@ -43,6 +44,7 @@ import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; import org.eclipse.m2e.core.internal.preferences.ProblemSeverity; import org.eclipse.m2e.core.lifecyclemapping.model.PluginExecutionAction; +import org.osgi.service.prefs.BackingStoreException; /** * Preference manager @@ -122,26 +124,10 @@ public void update(Preferences preferences) { } try { Settings mavenSettings = MavenPlugin.getMaven().getSettings(); - boolean oldDisableTest = false; String systemMmpd = System.getProperty(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY); - String oldMultiModuleProjectDirectory = systemMmpd; - List activeProfilesIds = mavenSettings.getActiveProfiles(); - for (org.apache.maven.settings.Profile settingsProfile : mavenSettings.getProfiles()) { - if ((settingsProfile.getActivation() != null && settingsProfile.getActivation().isActiveByDefault()) || activeProfilesIds.contains(settingsProfile.getId())) { - if (TRUE.equals(settingsProfile.getProperties().get(M2E_DISABLE_TEST_CLASSPATH_FLAG))) { - oldDisableTest = true; - } - if (systemMmpd == null) { - Object mmpd = settingsProfile.getProperties().get(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY); - if (mmpd instanceof String s) { - oldMultiModuleProjectDirectory = s; - } - } - if (oldDisableTest && oldMultiModuleProjectDirectory != null) { - break; - } - } - } + IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(IConstants.PLUGIN_ID); + boolean oldDisableTest = prefs.getBoolean(M2E_DISABLE_TEST_CLASSPATH_FLAG, false); + String oldMultiModuleProjectDirectory = prefs.get(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY, null); String multiModuleProjectDirectory = systemMmpd; if (multiModuleProjectDirectory == null) { if (preferences.getRootPaths() != null) { @@ -158,23 +144,32 @@ public void update(Preferences preferences) { } } } - if (!Objects.equals(multiModuleProjectDirectory, oldMultiModuleProjectDirectory) || (oldDisableTest != preferences.isMavenDisableTestClasspathFlag())) { - mavenSettings.getProfiles().removeIf(p -> JAVALS_PROFILE.equals(p.getId())); - if (preferences.isMavenDisableTestClasspathFlag() || multiModuleProjectDirectory != null) { - Profile profile = new Profile(); - profile.setId(JAVALS_PROFILE); - Activation activation = new Activation(); - activation.setActiveByDefault(true); - profile.setActivation(activation); - profile.getProperties().put(M2E_DISABLE_TEST_CLASSPATH_FLAG, String.valueOf(preferences.isMavenDisableTestClasspathFlag())); - if (multiModuleProjectDirectory != null) { - profile.getProperties().put(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY, multiModuleProjectDirectory); - } else { - profile.getProperties().remove(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY); - } - mavenSettings.addProfile(profile); - mavenSettings.addActiveProfile(profile.getId()); - updateMavenProjects = true; + updateMavenProjects = updateMavenProjects || !Objects.equals(multiModuleProjectDirectory, oldMultiModuleProjectDirectory) || (oldDisableTest != preferences.isMavenDisableTestClasspathFlag()); + mavenSettings.getProfiles().removeIf(p -> JAVALS_PROFILE.equals(p.getId())); + if (preferences.isMavenDisableTestClasspathFlag() || multiModuleProjectDirectory != null) { + Profile profile = new Profile(); + profile.setId(JAVALS_PROFILE); + Activation activation = new Activation(); + activation.setActiveByDefault(true); + profile.setActivation(activation); + profile.getProperties().put(M2E_DISABLE_TEST_CLASSPATH_FLAG, String.valueOf(preferences.isMavenDisableTestClasspathFlag())); + if (multiModuleProjectDirectory != null) { + profile.getProperties().put(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY, multiModuleProjectDirectory); + } else { + profile.getProperties().remove(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY); + } + mavenSettings.addProfile(profile); + mavenSettings.addActiveProfile(profile.getId()); + prefs.putBoolean(M2E_DISABLE_TEST_CLASSPATH_FLAG, preferences.isMavenDisableTestClasspathFlag()); + if (multiModuleProjectDirectory != null) { + prefs.put(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY, multiModuleProjectDirectory); + } else { + prefs.remove(MAVEN_MULTI_MODULE_PROJECT_DIRECTORY); + } + try { + prefs.flush(); + } catch (BackingStoreException e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); } } } catch (CoreException e) { @@ -198,9 +193,19 @@ public void update(Preferences preferences) { updateMavenProjects = true; } if (updateMavenProjects) { - for (IProject project : ProjectUtils.getAllProjects()) { - if (ProjectUtils.isMavenProject(project)) { - JavaLanguageServerPlugin.getProjectsManager().updateProject(project, true); + ProjectsManager projectManager = JavaLanguageServerPlugin.getProjectsManager(); + if (projectManager != null) { + if (projectManager.isBuildFinished()) { + projectManager.updateMavenProjects(null); + } else { + boolean hasMavenProjects = false; + for (IProject project : ProjectUtils.getAllProjects()) { + if (ProjectUtils.isMavenProject(project)) { + hasMavenProjects = true; + break; + } + } + projectManager.setShouldUpdateProjects(hasMavenProjects); } } } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandlerTest.java index a4888454fd..167d2ab515 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandlerTest.java @@ -122,6 +122,7 @@ public void setup() throws Exception { server = new JDTLanguageServer(projectsManager, preferenceManager, commandHandler); server.connectClient(client); JavaLanguageServerPlugin.getInstance().setProtocol(server); + JobHelpers.waitForJobsToComplete(monitor); } @After @@ -319,6 +320,7 @@ public void testWatchers() throws Exception { importProjects(Arrays.asList("maven/salut", "gradle/simple-gradle")); newEmptyProject(); List watchers = projectsManager.registerWatchers(); + projectsManager.projectsBuildFinished(null); // 8 basic + 3 project roots assertEquals("Unexpected watchers:\n" + toString(watchers), 12, watchers.size()); List projectWatchers = watchers.subList(9, 12);