diff --git a/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/pom.xml b/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/pom.xml index db79eadab23b..af51813e2187 100644 --- a/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/pom.xml +++ b/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/pom.xml @@ -38,6 +38,14 @@ commons-io commons-io + + javax.annotation + javax.annotation-api + + + javax.inject + javax.inject + javax.ws.rs javax.ws.rs-api @@ -86,6 +94,10 @@ org.eclipse.che.plugin che-plugin-java-server + + org.eclipse.lsp4j + org.eclipse.lsp4j + org.slf4j slf4j-api diff --git a/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectModule.java b/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectModule.java index 8cd048d38a78..857c67ef1f4b 100644 --- a/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectModule.java +++ b/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectModule.java @@ -32,5 +32,6 @@ protected void configure() { newSetBinder(binder(), ProjectHandler.class).addBinding().to(PlainJavaInitHandler.class); bind(ClasspathUpdaterService.class); + bind(PlainJavaProjectSourceFolderWatcher.class).asEagerSingleton(); } } diff --git a/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectSourceFolderWatcher.java b/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectSourceFolderWatcher.java new file mode 100644 index 000000000000..6994e46b3093 --- /dev/null +++ b/plugins/plugin-java/che-plugin-java-plain/che-plugin-java-plain-server/src/main/java/org/eclipse/che/plugin/java/plain/server/inject/PlainJavaProjectSourceFolderWatcher.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.plugin.java.plain.server.inject; + +import static java.nio.file.Files.isDirectory; +import static java.util.Collections.singletonList; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.prefixURI; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.removeUriScheme; +import static org.eclipse.che.jdt.ls.extension.api.Commands.GET_PROJECT_SOURCE_LOCATIONS_COMMAND; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.ExtendedLanguageServer; +import org.eclipse.che.api.languageserver.FindServer; +import org.eclipse.che.api.project.server.notification.ProjectUpdatedEvent; +import org.eclipse.che.api.watcher.server.FileWatcherManager; +import org.eclipse.che.api.watcher.server.impl.FileWatcherByPathMatcher; +import org.eclipse.che.plugin.java.inject.JavaModule; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.FileChangeType; +import org.eclipse.lsp4j.FileEvent; +import org.eclipse.lsp4j.services.LanguageServer; + +/** + * Reports the create/update/delete changes on project source folders to jdt.ls + * + * @author V. Rubezhny + */ +public class PlainJavaProjectSourceFolderWatcher { + private static final Gson gson = + new GsonBuilder().disableHtmlEscaping().serializeNulls().create(); + + private final FileWatcherManager manager; + private final FileWatcherByPathMatcher matcher; + private final FindServer lsRegistry; + + private final EventService eventService; + + private final CopyOnWriteArrayList watcherIds = new CopyOnWriteArrayList<>(); + + @Inject + public PlainJavaProjectSourceFolderWatcher( + FileWatcherManager manager, + FileWatcherByPathMatcher matcher, + FindServer lsRegistry, + EventService eventService) { + this.manager = manager; + this.matcher = matcher; + this.lsRegistry = lsRegistry; + this.eventService = eventService; + } + + @PostConstruct + protected void startWatchers() { + int watcherId = + manager.registerByMatcher( + folderMatcher(), + s -> report(s, FileChangeType.Created), + s -> {}, + s -> report(s, FileChangeType.Deleted)); + + watcherIds.add(watcherId); + eventService.subscribe(this::onProjectUpdated, ProjectUpdatedEvent.class); + } + + @PreDestroy + public void stopWatchers() { + watcherIds.stream().forEach(id -> manager.unRegisterByMatcher(id)); + } + + private void onProjectUpdated(ProjectUpdatedEvent event) { + ExecuteCommandParams params = + new ExecuteCommandParams( + GET_PROJECT_SOURCE_LOCATIONS_COMMAND, singletonList(prefixURI(event.getProjectPath()))); + + ExtendedLanguageServer languageServer = lsRegistry.byId(JavaModule.LS_ID); + if (languageServer == null) { + return; + } + + languageServer + .getServer() + .getWorkspaceService() + .executeCommand(params) + .thenAccept( + result -> { + if (result == null) { + return; + } + Type type = new TypeToken>() {}.getType(); + List paths = gson.fromJson(gson.toJson(result), type); + paths.stream().forEach(f -> matcher.accept(Paths.get(removeUriScheme(prefixURI(f))))); + }); + } + + private PathMatcher folderMatcher() { + return it -> isDirectory(it); + } + + private void report(String path, FileChangeType changeType) { + ExtendedLanguageServer languageServer = lsRegistry.byId(JavaModule.LS_ID); + if (languageServer != null) { + send(languageServer.getServer(), path, changeType); + } + } + + private void send(LanguageServer server, String path, FileChangeType changeType) { + DidChangeWatchedFilesParams params = + new DidChangeWatchedFilesParams( + Collections.singletonList(new FileEvent(prefixURI(path), changeType))); + server.getWorkspaceService().didChangeWatchedFiles(params); + } +} diff --git a/selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/plainjava/PlainJavaProjectConfigureClasspathTest.java b/selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/plainjava/PlainJavaProjectConfigureClasspathTest.java index 043be809b9fc..8e78941f1da6 100644 --- a/selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/plainjava/PlainJavaProjectConfigureClasspathTest.java +++ b/selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/plainjava/PlainJavaProjectConfigureClasspathTest.java @@ -30,6 +30,7 @@ import org.eclipse.che.selenium.core.workspace.TestWorkspace; import org.eclipse.che.selenium.pageobject.CodenvyEditor; import org.eclipse.che.selenium.pageobject.ConfigureClasspath; +import org.eclipse.che.selenium.pageobject.Consoles; import org.eclipse.che.selenium.pageobject.Ide; import org.eclipse.che.selenium.pageobject.Loader; import org.eclipse.che.selenium.pageobject.Menu; @@ -64,6 +65,7 @@ public class PlainJavaProjectConfigureClasspathTest { @Inject private Loader loader; @Inject private Menu menu; @Inject private TestProjectServiceClient testProjectServiceClient; + @Inject private Consoles consoles; @BeforeClass public void prepare() throws Exception { @@ -75,6 +77,7 @@ public void prepare() throws Exception { testProjectServiceClient.importProject( ws.getId(), Paths.get(resource.toURI()), LIB_PROJECT, ProjectTemplates.PLAIN_JAVA); ide.open(ws); + consoles.waitJDTLSProjectResolveFinishedMessage(PROJECT_NAME); } @Test