blob: 9fff73d08370cb86bb8273de06a9fbfb33e96702 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.incubator.internal.filters.core.server;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.launch.LSPLauncher;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.tracecompass.incubator.internal.filters.core.Activator;
import org.eclipse.tracecompass.incubator.internal.filters.core.shared.FilterLspConfiguration;
/**
* It's the class responsible for listening on the server socket and instantiate
* a language server instance to associate with a client
*
* @author Maxime Thibault
*/
public class FilterServerFactory {
private LanguageFilterServer fLSPServer;
private ServerSocket fServerSocket;
private Thread fMainThread;
private List<Thread> fClientThreads = new ArrayList<>();
private Boolean fCanRun = true;
/**
* Create serverSocket then wait for a client socket to connect When a
* client socket is connected, create the lspLauncher to listen to incoming
* requests
*
* @throws IOException
* can be thrown by the socket
*/
public FilterServerFactory() throws IOException {
fServerSocket = new ServerSocket(FilterLspConfiguration.PORT);
fMainThread = new Thread(new ServerLoop());
fMainThread.start();
}
/**
* Create server from InputStream and OutputStream FIXME: This class is used
* by the tests case only. This needs to be fixed.
*
* @param in
* InputStream (data in)
* @param out
* OutputStream (data out)
*/
@VisibleForTesting
public FilterServerFactory(InputStream in, OutputStream out) {
fLSPServer = new LanguageFilterServer();
Launcher<LanguageClient> launcher = LSPLauncher.createServerLauncher(fLSPServer, in, out);
fLSPServer.connect(launcher.getRemoteProxy());
launcher.startListening();
}
/**
* Thread target to be run each time a new connection is accepted
*
* @author Maxime Thibault
* @author Remi Croteau
*
*/
class ConnectionInitializer implements Runnable {
private Socket fClientSocket;
ConnectionInitializer(Socket clientSocket) {
fClientSocket = clientSocket;
}
@Override
public void run() {
try {
// Instantiate LSP client
InputStream in = fClientSocket.getInputStream();
OutputStream out = fClientSocket.getOutputStream();
fLSPServer = new LanguageFilterServer();
Launcher<LanguageClient> launcher = LSPLauncher.createServerLauncher(fLSPServer, in, out);
fLSPServer.connect(launcher.getRemoteProxy());
launcher.startListening();
} catch (IOException e) {
try {
fClientSocket.close();
} catch (IOException e1) {
// Nothing to do, closing the socket
}
Activator.getInstance().logError(e.getMessage());
}
}
}
/**
* Main server loop to accept new connections.
*
* @author Remi Croteau
*
*/
class ServerLoop implements Runnable {
@Override
public void run() {
while (fCanRun) {
try {
Socket clientSocket = fServerSocket.accept();
Thread thread = new Thread(new ConnectionInitializer(clientSocket));
fClientThreads.add(thread);
thread.start();
} catch (IOException e) {
Activator.getInstance().logError(e.getMessage());
}
}
try {
fServerSocket.close();
} catch (IOException e) {
// Nothing to do, just closing the socket
}
}
}
/**
* Close client-end and server socket
*
*/
public void dispose() {
fClientThreads.forEach((Thread t) -> {
t.interrupt();
});
fCanRun = false;
fMainThread.interrupt();
}
}