From 68949b3074c2ded2f19200c55033ebfdedb68120 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 20 Jan 2017 13:20:54 +0100 Subject: [PATCH] refactored startup code to launch language server as standalone app, listening for clients to connect, into commons code --- .../languageserver/LaunguageServerApp.java | 82 +++++++++++++++++-- .../ide/vscode/boot/java/Main.java | 4 +- .../ide/vscode/boot/java/StartupServer.java | 78 ------------------ .../springframework/ide/vscode/boot/Main.java | 2 +- .../ide/vscode/concourse/Main.java | 2 +- .../ide/vscode/manifest/yaml/Main.java | 2 +- 6 files changed, 81 insertions(+), 89 deletions(-) delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java index 3f4e51dd4..f62aef438 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java @@ -15,10 +15,16 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; +import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.Channels; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; @@ -31,6 +37,7 @@ import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClientAware; import org.eclipse.lsp4j.services.LanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.LoggingFormat; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; /** @@ -38,24 +45,44 @@ import org.springframework.ide.vscode.commons.languageserver.util.LoggingFormat; * write to create suitable 'main' method to launch a language server. *

* The easiest way to use this is to create your own static main method. - * Then call this class's start method with a Provider as + * Then call this class's start method with a Provider as * a argument. *

* Alternatively, you can also subclass it and implement the abstract * `createServer` method. * * @author Kris De Volder + * @author Martin Lippert */ public abstract class LaunguageServerApp { - public static void start(Provider languageServerFactory) throws IOException { + private static final String STANDALONE_STARTUP = "standalone-startup"; + private static final int SERVER_STANDALONE_PORT = 5007; + + public static void start(Provider languageServerFactory) throws IOException, InterruptedException { LaunguageServerApp app = new LaunguageServerApp() { @Override - protected LanguageServer createServer() { + protected SimpleLanguageServer createServer() { return languageServerFactory.get(); } }; - app.start(); + + if (System.getProperty(STANDALONE_STARTUP, "false").equals("true")) { + app.startAsServer(); + } + else { + app.start(); + } + } + + public static void startAsServer(Provider languageServerFactory) throws IOException, InterruptedException { + LaunguageServerApp app = new LaunguageServerApp() { + @Override + protected SimpleLanguageServer createServer() { + return languageServerFactory.get(); + } + }; + app.startAsServer(); } protected static class Connection { @@ -112,7 +139,50 @@ public abstract class LaunguageServerApp { } } - private static Connection connectToNode() throws IOException { + /** + * starts up the language server and let it listen for connections from the outside + * instead of connecting itself to an existing port or channel. + * + * This is meant for development only, to reduce turnaround times while working + * on the language server from within an IDE, so that you can start the language + * server right away in debug mode and let the vscode extension connect to that + * instance instead of vice versa. + * + * 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"); + + Function wrapper = consumer -> { + MessageConsumer result = consumer; + return result; + }; + + SimpleLanguageServer languageServer = createServer(); + Launcher launcher = createSocketLauncher(languageServer, STS4LanguageClient.class, + new InetSocketAddress("localhost", SERVER_STANDALONE_PORT), Executors.newCachedThreadPool(), wrapper); + + languageServer.connect(launcher.getRemoteProxy()); + Future future = launcher.startListening(); + while (!future.isDone()) { + Thread.sleep(10_000l); + } + } + + 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 static Connection connectToNode() throws IOException { String port = System.getProperty("server.port"); if (port != null) { @@ -172,7 +242,7 @@ public abstract class LaunguageServerApp { launcher.startListening().get(); } - protected abstract LanguageServer createServer(); + protected abstract SimpleLanguageServer createServer(); } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java index 73e1103bd..d8b545050 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * 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 @@ -23,7 +23,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage */ public class Main { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { JavaProjectFinder javaProjectFinder = BootJavaLanguageServer.DEFAULT_PROJECT_FINDER; SimpleLanguageServer server = new BootJavaLanguageServer(javaProjectFinder); diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java deleted file mode 100644 index 7ba13b8c7..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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.java; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.Channels; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.function.Function; - -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; - -/** - * starts up the language server and let it listen for connections from the outside - * instead of connecting itself to an existing port or channel. - * - * This is meant for development only, to reduce turnaround times while working - * on the language server from within an IDE, so that you can start the language - * server right away in debug mode and let the vscode extension connect to that - * instance instead of vice versa. - * - * 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 - * - * @author Martin Lippert - */ -public class StartupServer { - - public static void main(String[] args) throws InterruptedException, IOException { - - JavaProjectFinder javaProjectFinder = BootJavaLanguageServer.DEFAULT_PROJECT_FINDER; - SimpleLanguageServer languageServer = new BootJavaLanguageServer(javaProjectFinder); - - Function wrapper = consumer -> { - MessageConsumer result = consumer; - return result; - }; - - Launcher launcher = createSocketLauncher(languageServer, STS4LanguageClient.class, new InetSocketAddress("localhost", 5007), Executors.newCachedThreadPool(), wrapper); - - languageServer.connect(launcher.getRemoteProxy()); - Future future = launcher.startListening(); - while (!future.isDone()) { - Thread.sleep(10_000l); - } - } - - private static 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; - } - -} diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/Main.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/Main.java index 0b3391015..e77b0f41e 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/Main.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/Main.java @@ -29,7 +29,7 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; */ public class Main { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { JavaProjectFinder javaProjectFinder = BootPropertiesLanguageServer.DEFAULT_PROJECT_FINDER; DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/Main.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/Main.java index bd8631914..af1006194 100644 --- a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/Main.java +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/Main.java @@ -19,7 +19,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage public class Main { SimpleLanguageServer server = new ConcourseLanguageServer(); - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(ConcourseLanguageServer::new); } } diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java index e072d659e..4c1ae9c84 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/Main.java @@ -18,7 +18,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage public class Main { SimpleLanguageServer server = new ManifestYamlLanguageServer(); - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(ManifestYamlLanguageServer::new); } }