From 6717306d8f11a1bbdf487bc14a4965cc0c0e933c Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 2 Oct 2018 15:39:15 -0700 Subject: [PATCH] Convert language servers to Boot apps --- .../bosh-language-server/pom.xml | 14 +-- .../ide/vscode/bosh/BoshCliConfig.java | 2 + .../ide/vscode/bosh/BoshDefintionFinder.java | 27 +++-- ...in.java => BoshLanguageServerBootApp.java} | 33 ++++-- .../boot/app/cli/LocalSpringBootApp.java | 1 - .../languageserver/LaunguageServerApp.java | 106 ++++++++++-------- .../definition/SimpleDefinitionFinder.java | 35 ++---- .../util/SimpleLanguageServer.java | 8 +- .../ide/vscode/commons/util/AsyncRunner.java | 1 - .../ide/vscode/commons/util/LogRedirect.java | 14 ++- .../commons/language-server-starter/pom.xml | 28 +++++ .../starter/LanguageServerAutoconf.java | 28 +++++ headless-services/commons/pom.xml | 15 ++- .../concourse-language-server/pom.xml | 8 +- .../concourse/ConcourseDefinitionFinder.java | 28 +++-- .../ConcourseLanguageServerBootApp.java | 37 ++++++ .../ide/vscode/concourse/Main.java | 34 ------ .../github/DefaultGithubInfoProvider.java | 10 +- .../src/main/resources/application.yml | 0 .../manifest-yaml-language-server/pom.xml | 17 +++ .../ide/vscode/manifest/yaml/Main.java | 28 ----- .../ManifestYamlLanguageServerBootApp.java | 37 ++++++ .../src/main/resources/application.yml | 0 .../spring-boot-language-server/pom.xml | 13 ++- .../vscode/boot/BootLanguagServerBootApp.java | 36 ++++++ .../springframework/ide/vscode/boot/Main.java | 35 ------ .../src/test/resources/logback-test.xml | 15 +++ .../commons-vscode/src/launch-util.ts | 3 +- vscode-extensions/vscode-concourse/build.sh | 8 +- .../vscode-manifest-yaml/build.sh | 7 ++ 30 files changed, 394 insertions(+), 234 deletions(-) rename headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/{Main.java => BoshLanguageServerBootApp.java} (57%) create mode 100644 headless-services/commons/language-server-starter/pom.xml create mode 100644 headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoconf.java create mode 100644 headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java delete mode 100644 headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/Main.java create mode 100644 headless-services/concourse-language-server/src/main/resources/application.yml delete mode 100644 headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java create mode 100644 headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java create mode 100644 headless-services/manifest-yaml-language-server/src/main/resources/application.yml create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguagServerBootApp.java delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/logback-test.xml create mode 100755 vscode-extensions/vscode-manifest-yaml/build.sh diff --git a/headless-services/bosh-language-server/pom.xml b/headless-services/bosh-language-server/pom.xml index a84c4852c..ee0b9621e 100644 --- a/headless-services/bosh-language-server/pom.xml +++ b/headless-services/bosh-language-server/pom.xml @@ -16,15 +16,13 @@ ${project.version} - - - distribution-repository - Temporary Staging Repository - file://${basedir}/dist - - - + + + org.springframework.ide.vscode + language-server-starter + ${dependencies.version} + org.springframework.ide.vscode diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCliConfig.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCliConfig.java index 624c086bf..bd159abf9 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCliConfig.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCliConfig.java @@ -14,6 +14,7 @@ import java.time.Duration; import org.springframework.ide.vscode.commons.languageserver.util.Settings; import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.stereotype.Component; /** * Provides access to configuration options that allow user to @@ -22,6 +23,7 @@ import org.springframework.ide.vscode.commons.util.Log; * * @author Kris De Volder */ +@Component public class BoshCliConfig { /** diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDefintionFinder.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDefintionFinder.java index 1cbbabfad..6e091ada7 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDefintionFinder.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDefintionFinder.java @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.bosh; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -29,6 +30,9 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.yaml.snakeyaml.nodes.Node; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + import reactor.core.publisher.Flux; public class BoshDefintionFinder extends SimpleDefinitionFinder { @@ -44,7 +48,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder handle(Node refNode, TextDocument doc, YamlFileAST ast); + List handle(Node refNode, TextDocument doc, YamlFileAST ast); } public BoshDefintionFinder(BoshLanguageServer server, BoshSchemas schema, YamlAstCache asts, ASTTypeCache astTypes) { @@ -64,7 +68,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder findDefinitions(TextDocumentPositionParams params) { + public List handle(TextDocumentPositionParams params) { try { TextDocument doc = server.getTextDocumentService().get(params); if (doc!=null) { @@ -85,7 +89,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder candidates = astTypes.getNodes(uri, def); - return Flux.fromIterable(candidates) - .filter((node) -> name.equals(NodeUtil.asScalar(node))) - .map((node) -> toLocation(doc, node)) - .filter(Optional::isPresent) - .map(Optional::get); + Builder definitions = ImmutableList.builder(); + for (Node node : candidates) { + if (name.equals(NodeUtil.asScalar(node))) { + Optional loc = toLocation(doc, node); + if (loc.isPresent()) { + definitions.add(loc.get()); + } + } + } + return definitions.build(); } } - return Flux.empty(); + return ImmutableList.of(); }; handlers.put(ref, handler); } diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServerBootApp.java similarity index 57% rename from headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java rename to headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServerBootApp.java index 457be6e1e..7b1d88f47 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServerBootApp.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2018 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,24 +10,35 @@ *******************************************************************************/ package org.springframework.ide.vscode.bosh; -import java.io.IOException; - +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider; import org.springframework.ide.vscode.bosh.models.BoshCommandReleasesProvider; import org.springframework.ide.vscode.bosh.models.BoshCommandStemcellsProvider; -import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; import org.springframework.ide.vscode.commons.util.LogRedirect; -public class Main { - public static void main(String[] args) throws IOException, InterruptedException { - String serverName = "bosh-language-server"; - LogRedirect.redirectToFile(serverName); - BoshCliConfig cliConfig = new BoshCliConfig(); - LaunguageServerApp.start(serverName, () -> new BoshLanguageServer( +@SpringBootApplication +public class BoshLanguageServerBootApp { + + private static final String SERVER_NAME = "bosh-language-server"; + + public static void main(String[] args) throws Exception { + LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead. + SpringApplication.run(BoshLanguageServerBootApp.class, args); + } + + @Bean public String serverName() { + return SERVER_NAME; + } + + @Bean BoshLanguageServer languageServer(BoshCliConfig cliConfig) { + return new BoshLanguageServer( cliConfig, new BoshCommandCloudConfigProvider(cliConfig), new BoshCommandStemcellsProvider(cliConfig), new BoshCommandReleasesProvider(cliConfig) - )); + ); } + } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java index eb40cd5c6..590664c3b 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java @@ -16,7 +16,6 @@ import java.util.Collection; import java.util.Map.Entry; import java.util.Properties; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java index 32143283a..6959b1cd4 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java @@ -34,8 +34,11 @@ import org.eclipse.lsp4j.jsonrpc.MessageConsumer; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClientAware; import org.eclipse.lsp4j.services.LanguageServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.languageserver.util.LoggingFormat; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.util.AsyncRunner; import org.springframework.ide.vscode.commons.util.Log; @@ -53,37 +56,48 @@ import org.springframework.ide.vscode.commons.util.Log; * @author Kris De Volder * @author Martin Lippert */ -public abstract class LaunguageServerApp { +public class LaunguageServerApp { public static final String STS4_LANGUAGESERVER_NAME = "sts4.languageserver.name"; public static final String STANDALONE_STARTUP = "standalone-startup"; private static final int SERVER_STANDALONE_PORT = 5007; - public static void start(String name, Provider languageServerFactory) throws IOException, InterruptedException { - System.setProperty(STS4_LANGUAGESERVER_NAME, name); //makes it easy to recognize language server processes. - LaunguageServerApp app = new LaunguageServerApp() { - @Override - protected SimpleLanguageServer createServer() { - return languageServerFactory.get(); - } - }; + final static Logger log = LoggerFactory.getLogger(LaunguageServerApp.class); + private final String name; + private final Provider languageServerFactory; + + public LaunguageServerApp(String name, Provider languageServerFactory) { + super(); + this.name = name; + this.languageServerFactory = languageServerFactory; + } + + public void start() throws Exception { + System.setProperty(STS4_LANGUAGESERVER_NAME, name); //makes it easy to recognize language server processes. + LaunguageServerApp app = this; if (System.getProperty(STANDALONE_STARTUP, "false").equals("true")) { app.startAsServer(); - } - else { - app.start(); + } else { + app.startAsClient(); } } - public static void startAsServer(Provider languageServerFactory) throws IOException, InterruptedException { - LaunguageServerApp app = new LaunguageServerApp() { - @Override - protected SimpleLanguageServer createServer() { - return languageServerFactory.get(); - } - }; - app.startAsServer(); + public void startAsync() { + //TODO: feel a bit wasteful to have thread dedicated to just waiting for the server to stop. + // Not sure how we can really avoid this though. Lsp4j is providing + // lots of api that returns Futures which the only way to deal with them is blocking threads calling + // their get method. + new Thread( + () -> { + try { + start(); + } catch (Exception e) { + log.error("", e); + } + }, + "LanguageServerApp lifecycle" + ).start(); } protected static class Connection { @@ -122,14 +136,14 @@ public abstract class LaunguageServerApp { } } - public void start() throws IOException { + public void startAsClient() throws IOException { Log.info("Starting LS"); Connection connection = null; try { LoggingFormat.startLogging(); connection = connectToNode(); - run(connection); + runAsync(connection).get(); } catch (Throwable t) { Log.log(t); System.exit(1); @@ -152,8 +166,8 @@ public abstract class LaunguageServerApp { * Source of inspiration: * https://github.com/itemis/xtext-languageserver-example/blob/master/org.xtext.example.mydsl.ide/src/org/xtext/example/mydsl/ide/RunServer.java */ - public void startAsServer() throws IOException, InterruptedException { - Log.info("Starting LS as standlone server port = "+SERVER_STANDALONE_PORT); + public void startAsServer() throws Exception { + log.info("Starting LS as standlone server port = {}", SERVER_STANDALONE_PORT); Function wrapper = consumer -> { MessageConsumer result = consumer; @@ -165,33 +179,30 @@ public abstract class LaunguageServerApp { new InetSocketAddress("localhost", SERVER_STANDALONE_PORT), createServerThreads(), wrapper); languageServer.connect(launcher.getRemoteProxy()); - Future future = launcher.startListening(); - while (!future.isDone()) { - Thread.sleep(10_000l); - } + launcher.startListening().get(); } /** * Creates the thread pool / executor passed to lsp4j server intialization. From the looks of things, * @return */ - protected ExecutorService createServerThreads() { + protected ExecutorService createServerThreads() { return Executors.newCachedThreadPool(); } - private Launcher createSocketLauncher(Object localService, Class remoteInterface, SocketAddress socketAddress, ExecutorService executorService, Function wrapper) throws IOException { - AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open().bind(socketAddress); - AsynchronousSocketChannel socketChannel; - try { - socketChannel = serverSocket.accept().get(); - return Launcher.createIoLauncher(localService, remoteInterface, Channels.newInputStream(socketChannel), Channels.newOutputStream(socketChannel), executorService, wrapper); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - return null; - } + private Launcher createSocketLauncher( + Object localService, Class remoteInterface, + SocketAddress socketAddress, ExecutorService executorService, + Function wrapper + ) throws Exception { + AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open().bind(socketAddress); + AsynchronousSocketChannel socketChannel = serverSocket.accept().get(); + log.info("Client connected via socket"); + return Launcher.createIoLauncher(localService, remoteInterface, Channels.newInputStream(socketChannel), + Channels.newOutputStream(socketChannel), executorService, wrapper); + } - private static Connection connectToNode() throws IOException { + private static Connection connectToNode() throws IOException { String port = System.getProperty("server.port"); if (port != null) { @@ -217,10 +228,8 @@ public abstract class LaunguageServerApp { * Listen for requests from the parent node process. * Send replies asynchronously. * When the request stream is closed, wait for 5s for all outstanding responses to compute, then return. - * @throws ExecutionException - * @throws InterruptedException */ - protected void run(Connection connection) throws InterruptedException, ExecutionException { + protected Future runAsync(Connection connection) throws Exception { LanguageServer server = createServer(); ExecutorService executor = createServerThreads(); Function wrapper = (MessageConsumer consumer) -> { @@ -229,7 +238,7 @@ public abstract class LaunguageServerApp { consumer.consume(msg); } catch (UnsupportedOperationException e) { //log a warning and ignore. We are getting some messages from vsCode the server doesn't know about - Log.warn("Unsupported message was ignored!", e); + log.warn("Unsupported message was ignored!", e); } }; }; @@ -246,9 +255,10 @@ public abstract class LaunguageServerApp { ((LanguageClientAware) server).connect(client); } - launcher.startListening().get(); + return launcher.startListening(); } - protected abstract SimpleLanguageServer createServer(); - + final SimpleLanguageServer createServer() { + return languageServerFactory.get(); + } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/definition/SimpleDefinitionFinder.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/definition/SimpleDefinitionFinder.java index ec0eb88fa..dabd54b16 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/definition/SimpleDefinitionFinder.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/definition/SimpleDefinitionFinder.java @@ -10,8 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver.definition; +import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.TextDocumentPositionParams; @@ -20,7 +20,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.TextDocument; -import reactor.core.publisher.Flux; +import com.google.common.collect.ImmutableList; /** * {@link SimpleDefinitionFinder} provides a 'dummy' implementation of @@ -35,21 +35,7 @@ public class SimpleDefinitionFinder implements D } @Override - public List handle(TextDocumentPositionParams position) { - return findDefinitions(position) - .collect(Collectors.toList()).block(); - } - - /** - * This is meant to be overridden by subclass. This method provides a simple implementation - * of 'goto definition' (which is not one you probably want to use in practice, but it - * might be usful just to test whether things are wired up correctly to make the - * 'goto definition' action in vscode work. - *

- * The implementation provided here simply looks for the first occurrence of the word - * currently pointed at in the current document using String.indexOf. - */ - protected Flux findDefinitions(TextDocumentPositionParams params) { + public List handle(TextDocumentPositionParams params) { try { TextDocument doc = server.getTextDocumentService().get(params); if (doc != null) { @@ -68,20 +54,17 @@ public class SimpleDefinitionFinder implements D String text = doc.get(); int def = text.indexOf(word); if (def>=0) { - return Flux.just( - new Location(params.getTextDocument().getUri(), - doc.toRange(def, word.length()) - ) - ) - .doOnNext((Location loc) -> { - Log.log("definition: "+loc); - }); + Location loc = new Location(params.getTextDocument().getUri(), + doc.toRange(def, word.length()) + ); + Log.log("definition: "+loc); + return ImmutableList.of(loc); } } } catch (Exception e) { Log.log(e); } - return Flux.empty(); + return Collections.emptyList(); } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 4375bc31d..d7b6a98c7 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -144,7 +144,11 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA private Map commands = new HashMap<>(); - private AsyncRunner async = new AsyncRunner(Schedulers.newSingle("SimpleLanguaserver main thread")); + private AsyncRunner async = new AsyncRunner(Schedulers.newSingle(runable -> { + Thread t = new Thread(runable, "SimpleLanguaserver main thread"); + t.setDaemon(true); + return t; + })); private ClasspathListenerManager classpathListenerManager; @Override @@ -214,7 +218,7 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA @Override public CompletableFuture initialize(InitializeParams params) { - Log.debug("Initializing: "+params); + log.info("Initializing: "+params); // multi-root workspace handling List workspaceFolders = getWorkspaceFolders(params); diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/AsyncRunner.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/AsyncRunner.java index 7c18da400..546f4d547 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/AsyncRunner.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/AsyncRunner.java @@ -18,7 +18,6 @@ import org.slf4j.Logger; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; -import reactor.core.scheduler.Schedulers; public class AsyncRunner { diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/LogRedirect.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/LogRedirect.java index 1200c15cc..893e6f619 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/LogRedirect.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/LogRedirect.java @@ -17,12 +17,24 @@ import java.io.PrintStream; public class LogRedirect { + public static void bootRedirectToFile(String name) throws IOException { + String logfilePath = System.getProperty("sts.log.file"); + if (StringUtil.hasText(logfilePath)) { + File logfile = new File(logfilePath); + System.err.println("Redirecting log output to: "+logfile); + PrintStream logFile = new PrintStream(new FileOutputStream(logfile, false)); + System.setErr(logFile); + System.setOut(logFile); //Spring boot actually logs on sysout instead of syserr. + } + } + public static void redirectToFile(String name) throws IOException { String logfilePath = System.getProperty("sts.log.file"); if (StringUtil.hasText(logfilePath)) { File logfile = new File(logfilePath); System.err.println("Redirecting log output to: "+logfile); - System.setErr(new PrintStream(new FileOutputStream(logfile, false))); + PrintStream logFile = new PrintStream(new FileOutputStream(logfile, false)); + System.setErr(logFile); } } diff --git a/headless-services/commons/language-server-starter/pom.xml b/headless-services/commons/language-server-starter/pom.xml new file mode 100644 index 000000000..c8e0212c3 --- /dev/null +++ b/headless-services/commons/language-server-starter/pom.xml @@ -0,0 +1,28 @@ + + 4.0.0 + language-server-starter + language-server-starter + Spring Boot Starter for building Language Server + + + org.springframework.ide.vscode + commons-parent + 1.1.0-SNAPSHOT + ../pom.xml + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.ide.vscode + commons-language-server + ${project.version} + + + diff --git a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoconf.java b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoconf.java new file mode 100644 index 000000000..41e5d874d --- /dev/null +++ b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoconf.java @@ -0,0 +1,28 @@ +package org.springframework.ide.vscode.languageserver.starter; + +import javax.inject.Provider; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; + +@Configuration +public class LanguageServerAutoconf { + + @Bean public LaunguageServerApp serverApp( + @Qualifier("serverName") String serverName, + Provider languageServerFactory + ) { + return new LaunguageServerApp(serverName, languageServerFactory); + } + + @Bean public CommandLineRunner serverStarter(LaunguageServerApp serverApp) { + return args -> { + serverApp.startAsync(); + }; + } + +} diff --git a/headless-services/commons/pom.xml b/headless-services/commons/pom.xml index bf496f260..ef8c8e2cf 100644 --- a/headless-services/commons/pom.xml +++ b/headless-services/commons/pom.xml @@ -9,6 +9,12 @@ 1.1.0-SNAPSHOT commons-parent + + org.springframework.boot + spring-boot-starter-parent + 2.0.5.RELEASE + + commons-language-server language-server-test-harness @@ -20,6 +26,8 @@ commons-maven commons-gradle commons-boot-app-cli + + language-server-starter @@ -74,14 +82,13 @@ 1.17 4.11 3.5.2 - 1.7.22 + 1.7.25 19.0 1.10.19 2.5.0 2.10 0.4.0-SNAPSHOT 3.2.7 - 2.0.4.RELEASE 3.8.0.RELEASE 3.1.5.RELEASE @@ -111,11 +118,11 @@ slf4j-api ${slf4j-version} - + junit junit diff --git a/headless-services/concourse-language-server/pom.xml b/headless-services/concourse-language-server/pom.xml index e33a2a8c9..57f23d894 100644 --- a/headless-services/concourse-language-server/pom.xml +++ b/headless-services/concourse-language-server/pom.xml @@ -25,10 +25,16 @@ + + + org.springframework.boot + spring-boot-starter-actuator + + org.springframework.ide.vscode - commons-language-server + language-server-starter ${dependencies.version} diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java index 5ed97598c..5ba1e04a2 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java @@ -10,7 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.concourse; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -28,13 +30,16 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.yaml.snakeyaml.nodes.Node; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + import reactor.core.publisher.Flux; public class ConcourseDefinitionFinder extends SimpleDefinitionFinder { @FunctionalInterface private interface Handler { - Flux handle(Node refNode, TextDocument doc, YamlFileAST ast); + List handle(Node refNode, TextDocument doc, YamlFileAST ast); } private final ASTTypeCache astTypes; @@ -64,19 +69,24 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder { String name = NodeUtil.asScalar(refNode); if (name!=null) { - return Flux.fromStream(definitionsPath.traverseAmbiguously(ast)) - .filter((node) -> name.equals(NodeUtil.asScalar(node))) - .map((node) -> toLocation(doc, node)) - .filter(Optional::isPresent) - .map(Optional::get); + Builder definitions = ImmutableList.builder(); + definitionsPath.traverseAmbiguously(ast).forEach(node -> { + if (name.equals(NodeUtil.asScalar(node))) { + Optional loc = toLocation(doc, node); + if (loc.isPresent()) { + definitions.add(loc.get()); + } + } + }); + return definitions.build(); } - return Flux.empty(); + return ImmutableList.of(); }; handlers.put(refType, handler); } @Override - protected Flux findDefinitions(TextDocumentPositionParams params) { + public List handle(TextDocumentPositionParams params) { try { TextDocument doc = server.getTextDocumentService().get(params); if (doc!=null) { @@ -97,7 +107,7 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder toLocation(TextDocument doc, Node node) { diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java new file mode 100644 index 000000000..f3dc47ae1 --- /dev/null +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.concourse; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.ide.vscode.commons.util.LogRedirect; +import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions; +import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider; + +@SpringBootApplication +public class ConcourseLanguageServerBootApp { + + private static final String SERVER_NAME = "concourse-language-server"; + + public static void main(String[] args) throws Exception { + LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead. + SpringApplication.run(ConcourseLanguageServerBootApp.class, args); + } + + @Bean public String serverName() { + return SERVER_NAME; + } + + @Bean ConcourseLanguageServer languageServer() { + return new ConcourseLanguageServer(YamlCompletionEngineOptions.DEFAULT, new DefaultGithubInfoProvider()); + } +} diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/Main.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/Main.java deleted file mode 100644 index 7aaadc68e..000000000 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/Main.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ - -package org.springframework.ide.vscode.concourse; - -import java.io.IOException; - -import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; -import org.springframework.ide.vscode.commons.util.LogRedirect; -import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions; -import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider; -import org.springframework.ide.vscode.concourse.github.GithubInfoProvider; - -import static org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp.STANDALONE_STARTUP; - -public class Main { - private static final YamlCompletionEngineOptions OPTIONS = YamlCompletionEngineOptions.DEFAULT; - - public static void main(String[] args) throws IOException, InterruptedException { - String serverName = "concourse-language-server"; - if (!Boolean.getBoolean(STANDALONE_STARTUP)) { - LogRedirect.redirectToFile(serverName); - } - LaunguageServerApp.start(serverName, () -> new ConcourseLanguageServer(OPTIONS, new DefaultGithubInfoProvider())); - } -} diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/github/DefaultGithubInfoProvider.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/github/DefaultGithubInfoProvider.java index e27068c1a..e3133b3ae 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/github/DefaultGithubInfoProvider.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/github/DefaultGithubInfoProvider.java @@ -128,11 +128,11 @@ public class DefaultGithubInfoProvider implements GithubInfoProvider { return reposByOwner.get(ownerName, loader(() -> { GHPerson owner = getOwner(ownerName); if (owner!=null) { - return Flux.fromIterable(owner.listRepositories()) - .filter(repo -> repo.getOwnerName().equals(ownerName)) - .map(GHRepository::getName) - .collect(CollectorUtil.toImmutableSet()) - .block(); + ImmutableList.Builder builder = ImmutableList.builder(); + for (GHRepository repo : owner.listRepositories()) { + builder.add(repo.getName()); + } + return builder.build(); } return null; })) diff --git a/headless-services/concourse-language-server/src/main/resources/application.yml b/headless-services/concourse-language-server/src/main/resources/application.yml new file mode 100644 index 000000000..e69de29bb diff --git a/headless-services/manifest-yaml-language-server/pom.xml b/headless-services/manifest-yaml-language-server/pom.xml index 91fbe6757..a8d1c6c34 100644 --- a/headless-services/manifest-yaml-language-server/pom.xml +++ b/headless-services/manifest-yaml-language-server/pom.xml @@ -34,6 +34,12 @@ + + + org.springframework.ide.vscode + language-server-starter + ${dependencies.version} + org.springframework.ide.vscode @@ -47,6 +53,17 @@ ${dependencies.version} + + io.projectreactor + reactor-core + ${reactor-version} + + + io.projectreactor.ipc + reactor-netty + ${reactor-netty} + + org.springframework.ide.vscode commons-cf diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java deleted file mode 100644 index 3bfafd1e1..000000000 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.manifest.yaml; - -import java.io.IOException; - -import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; -import org.springframework.ide.vscode.commons.util.LogRedirect; - -public class Main { - SimpleLanguageServer server = new ManifestYamlLanguageServer(); - - public static void main(String[] args) throws IOException, InterruptedException { - String serverName = "manifest-yaml-language-server"; - LogRedirect.redirectToFile(serverName); - LaunguageServerApp.start(serverName, ManifestYamlLanguageServer::new); - } - -} diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java new file mode 100644 index 000000000..8e91eb914 --- /dev/null +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.manifest.yaml; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.ide.vscode.commons.util.LogRedirect; +import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions; + +@SpringBootApplication +public class ManifestYamlLanguageServerBootApp { + + private static final String SERVER_NAME = "manifest-yaml-language-server"; + + public static void main(String[] args) throws Exception { + LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead. + SpringApplication.run(ManifestYamlLanguageServerBootApp.class, args); + } + + @Bean public String serverName() { + return SERVER_NAME; + } + + @Bean ManifestYamlLanguageServer languageServer() { + return new ManifestYamlLanguageServer(); + } + +} diff --git a/headless-services/manifest-yaml-language-server/src/main/resources/application.yml b/headless-services/manifest-yaml-language-server/src/main/resources/application.yml new file mode 100644 index 000000000..e69de29bb diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml index 4b498abbf..ccf806f33 100644 --- a/headless-services/spring-boot-language-server/pom.xml +++ b/headless-services/spring-boot-language-server/pom.xml @@ -24,8 +24,14 @@ - + + + org.springframework.ide.vscode + language-server-starter + ${dependencies.version} + + org.springframework.ide.eclipse @@ -62,11 +68,6 @@ commons-boot-app-cli ${dependencies.version} - - org.springframework.boot - spring-boot - ${boot-version} - org.eclipse.jdt org.eclipse.jdt.core diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguagServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguagServerBootApp.java new file mode 100644 index 000000000..52285deb6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguagServerBootApp.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.util.LogRedirect; + +@SpringBootApplication +public class BootLanguagServerBootApp { + private static final String SERVER_NAME = "boot-language-server"; + + public static void main(String[] args) throws Exception { + LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead. + SpringApplication.run(BootLanguagServerBootApp.class, args); + } + + @Bean public String serverName() { + return SERVER_NAME; + } + + @Bean SimpleLanguageServer languageServer() { + return BootLanguageServer.create(BootLanguageServerParams.createDefault()).getServer(); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java deleted file mode 100644 index 4740eb784..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot; - -import java.io.IOException; - -import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; -import org.springframework.ide.vscode.commons.util.LogRedirect; - -/** - * Starts up Language Server process - * - * @author Alex Boyko - * @author Kris De Volder - * - */ -public class Main { - - public static void main(String[] args) throws IOException, InterruptedException { - String serverName = "boot-language-server"; - LogRedirect.redirectToFile(serverName); - LaunguageServerApp.start(serverName, - () -> BootLanguageServer.create(BootLanguageServerParams.createDefault()).getServer() - ); - } - -} diff --git a/headless-services/spring-boot-language-server/src/test/resources/logback-test.xml b/headless-services/spring-boot-language-server/src/test/resources/logback-test.xml new file mode 100644 index 000000000..303f30396 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + System.err + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index 880ca9b3b..e528e15e9 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -126,7 +126,8 @@ export function activate(options: ActivatorOptions, context: VSCode.ExtensionCon '-Dspring.lsp.client-port='+port, '-Dserver.port=' + port, '-Dsts.lsp.client=vscode', - '-Dsts.log.file=' + logfile + '-Dsts.log.file=' + logfile, //old style log redirect + '-Dlogging.file=' + logfile // spring boot log redirect ]; if (options.checkjvm) { options.checkjvm(context, jvm); diff --git a/vscode-extensions/vscode-concourse/build.sh b/vscode-extensions/vscode-concourse/build.sh index 1423ec8bf..2d14af6cb 100755 --- a/vscode-extensions/vscode-concourse/build.sh +++ b/vscode-extensions/vscode-concourse/build.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -#if [ ! -d "node_modules/commons-vscode" ]; then - ./scripts/preinstall.sh -#fi npm install -npm run vsce-package \ No newline at end of file +npm run vsce-package +rm -fr ~/.vscode/extensions/pivotal.vscode-concourse* +rm -fr ~/.vscode/extensions/.obsolete +code --install-extension vscode-concourse-*.vsix \ No newline at end of file diff --git a/vscode-extensions/vscode-manifest-yaml/build.sh b/vscode-extensions/vscode-manifest-yaml/build.sh new file mode 100755 index 000000000..8fa38e920 --- /dev/null +++ b/vscode-extensions/vscode-manifest-yaml/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e +npm install +npm run vsce-package +rm -fr ~/.vscode/extensions/pivotal.vscode-manifest-yaml* +rm -fr ~/.vscode/extensions/.obsolete +code --install-extension vscode-*.vsix \ No newline at end of file