diff --git a/README.md b/README.md index 3d525c0900..5cbfd131e3 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ [![Build Status](https://travis-ci.org/gorkem/java-language-server.svg?branch=master)](https://travis-ci.org/gorkem/java-language-server) -This repository contains only the server implementation. +This repository contains only the server implementation. For Visual Studio Code extension that uses this server visit [vscode-java](https://github.com/gorkem/vscode-java) ========================= -java-language-server +java-language-server ===================== java-language-server is a server implementation that provides Java language smartness. -The server adheres to the [language server protocol](https://github.com/Microsoft/language-server-protocol) -and can be used with any editor that supports the protocol. The server utilizes [Eclipse +The server adheres to the [language server protocol](https://github.com/Microsoft/language-server-protocol) +and can be used with any editor that supports the protocol. The server utilizes [Eclipse JDT](http://www.eclipse.org/jdt/), [M2Eeclipse](http://www.eclipse.org/m2e/). -Features +Features -------------- * As you type reporting of parsing and compilation errors * Code completion -* Javadoc hovers +* Javadoc hovers * Code outline * Code navigation * Code lens (references) @@ -30,7 +30,7 @@ First Time Setup -------------- 0. Fork and clone the repository 1. Install Eclipse [Neon Java EE](http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/neonr) -that will have most needed already installed. Alternately, +that will have most needed already installed. Alternately, you can get the [Eclipse IDE for Java developers](http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/neonr) and just instal Eclipse PDE from marketplace. @@ -49,9 +49,34 @@ Building from command line 2. This command will build the server into `/org.jboss.tools.vscode.product/target/repository` folder: ```bash - $ mvn clean verify + $ mvn clean verify ```` +Managing connection types +------------------------- +Java Language server supports socket and named pipes to communicate with the client. +Client can communicate its preferred connection methods by setting up environment +variables +* For using named pipes set the following environment variables before starting +the server. +``` +STDIN_PIPE_NAME --> where client reads from +STDOUT_PIPE_NAME --> where client writes to +``` +* For using plain sockets set the following environment variables before starting the +server. +``` +STDIN_PORT --> client reads +STDOUT_PORT --> client writes to +``` +optionally you can set host values for socket connections +``` +STDIN_HOST +STDOUT_HOST +``` +For both connection types the client is expected to create the connections +and wait for server the connect. + Feedback --------- diff --git a/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/base/LSPServer.java b/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/base/LSPServer.java index 23555c9ff4..f0bcd398ca 100644 --- a/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/base/LSPServer.java +++ b/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/base/LSPServer.java @@ -24,6 +24,7 @@ import org.jboss.tools.langs.base.ResponseError.ReservedCode; import org.jboss.tools.langs.transport.Connection; import org.jboss.tools.langs.transport.NamedPipeConnection; +import org.jboss.tools.langs.transport.SocketConnection; import org.jboss.tools.langs.transport.TransportMessage; import org.jboss.tools.vscode.internal.ipc.CancelMonitor; import org.jboss.tools.vscode.internal.ipc.NotificationHandler; @@ -145,20 +146,34 @@ protected LSPServer(){ public void connect() throws IOException{ this.notificationHandlers = buildNotificationHandlers(); this.requestHandlers = buildRequestHandlers(); - - final String stdInName = System.getenv("STDIN_PIPE_NAME"); - final String stdOutName = System.getenv("STDOUT_PIPE_NAME"); - if (stdInName == null || stdOutName == null) { - //XXX temporary hack to let unit tests run - System.err.println("Unable to connect to named pipes"); + initExecutor(); + connection = initConnection(); + if(connection == null ){ + //Temporary for tests to run + System.err.println("Failied to initialize connection"); return; } - initExecutor(); - connection = new NamedPipeConnection(stdOutName, stdInName); connection.start(); startDispatching(); } + + private Connection initConnection(){ + final String stdInName = System.getenv("STDIN_PIPE_NAME"); + final String stdOutName = System.getenv("STDOUT_PIPE_NAME"); + if (stdInName != null && stdOutName != null) { + return new NamedPipeConnection(stdOutName, stdInName); + } + final String wHost = System.getenv().getOrDefault("STDIN_HOST","localhost"); + final String rHost = System.getenv().getOrDefault("STDOUT_HOST","localhost"); + final String wPort = System.getenv().get("STDIN_PORT"); + final String rPort = System.getenv().get("STDOUT_PORT"); + if(rPort != null && wPort != null ){ + return new SocketConnection(rHost, Integer.parseInt(rPort), wHost, Integer.parseInt(wPort)); + } + return null; + } + /** * */ diff --git a/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/transport/SocketConnection.java b/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/transport/SocketConnection.java new file mode 100644 index 0000000000..666293eec6 --- /dev/null +++ b/org.jboss.tools.vscode.ipc/src/org/jboss/tools/langs/transport/SocketConnection.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * 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: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.jboss.tools.langs.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.Charset; + +import org.jboss.tools.vscode.internal.ipc.IPCPlugin; + +public class SocketConnection extends AbstractConnection { + + + private class ReaderThread extends Thread{ + + private InputStream stream; + + public ReaderThread() { + super("LSP_ReaderThread"); + } + + @Override + public void run() { + try { + stream = connectReadChannel(); + startWriterThread(); + } catch (IOException e1) { + //TODO: write failed to connect. + } + + while(true){ + TransportMessage message; + try { + message = TransportMessage.fromStream(stream, DEFAULT_CHARSET); + if(message == null ){ + //Stream disconnected exit reader thread + IPCPlugin.logError("Empty message read"); + break; + } + inboundQueue.add(message); + } catch (IOException e) { + //continue + } + } + } + } + + private class WriterThread extends Thread{ + + private OutputStream stream; + + public WriterThread() { + super("LSP_WriterThread"); + } + + @Override + public void run() { + try { + stream = connectWriteChannel(); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + while (true) { + try { + TransportMessage message = outboundQueue.take(); + message.send(stream, DEFAULT_CHARSET); + stream.flush(); + } catch (InterruptedException e) { + break;//exit + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private final String readHost; + private final String writeHost; + private final int readPort; + private final int writePort; + + private Socket writeSocket; + private Socket readSocket; + + private ServerSocket server; + + public SocketConnection(String readHost, int readPort , String writeHost, int writePort) { + this.readHost = readHost; + this.readPort = readPort; + this.writeHost = writeHost; + this.writePort = writePort; + } + + @Override + public void start() throws IOException { + ReaderThread readerThread = new ReaderThread(); + readerThread.setDaemon(true); + readerThread.start(); + } + + public void startWriterThread() throws IOException{ + + WriterThread writerThread = new WriterThread(); + writerThread.setDaemon(true); + writerThread.start(); + } + + @Override + public void close() { + try { + if(readSocket != null) + readSocket.close(); + if(writeSocket != null ) + writeSocket.close(); + if(server != null) + server.close(); + } catch (IOException e) { + // TODO: handle exception + } + } + + private InputStream connectReadChannel() throws IOException{ + readSocket = new Socket(readHost,readPort); + return readSocket.getInputStream(); + } + + private OutputStream connectWriteChannel() throws IOException{ + writeSocket = new Socket(writeHost, writePort); + return writeSocket.getOutputStream(); + + } + +}