diff --git a/plugins/org.eclipse.glsp.server.websocket/src/org/eclipse/glsp/server/websocket/WebsocketServerLauncher.java b/plugins/org.eclipse.glsp.server.websocket/src/org/eclipse/glsp/server/websocket/WebsocketServerLauncher.java index 928f2499..617c106c 100644 --- a/plugins/org.eclipse.glsp.server.websocket/src/org/eclipse/glsp/server/websocket/WebsocketServerLauncher.java +++ b/plugins/org.eclipse.glsp.server.websocket/src/org/eclipse/glsp/server/websocket/WebsocketServerLauncher.java @@ -29,9 +29,6 @@ import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; -import com.google.inject.Guice; -import com.google.inject.Injector; - public class WebsocketServerLauncher extends GLSPServerLauncher { private static Logger LOG = Logger.getLogger(WebsocketServerLauncher.class); private Server server; @@ -41,6 +38,7 @@ public class WebsocketServerLauncher extends GLSPServerLauncher { public WebsocketServerLauncher(final GLSPModule module, final String endpointPath) { super(module); this.endpointPath = endpointPath.startsWith("/") ? endpointPath.substring(1) : endpointPath; + addAdditionalModules(new WebsocketModule()); } public WebsocketServerLauncher(final GLSPModule module, final String endpointPath, final String clientAppPath) { @@ -48,14 +46,9 @@ public WebsocketServerLauncher(final GLSPModule module, final String endpointPat this.clientAppPath = clientAppPath; } - @Override - protected Injector doSetup() { - return Guice.createInjector(getGLSPModule(), new WebsocketModule()); - } - @Override @SuppressWarnings("checkstyle:IllegalCatch") - public void run(final String hostname, final int port) { + public void start(final String hostname, final int port) { try { // Setup Jetty Server server = new Server(new InetSocketAddress(hostname, port)); @@ -82,7 +75,7 @@ public void run(final String hostname, final int port) { ServerContainer container = WebSocketServerContainerInitializer.configureContext(webAppContext); ServerEndpointConfig.Builder builder = ServerEndpointConfig.Builder.create(GLSPServerEndpoint.class, "/" + endpointPath); - builder.configurator(new GLSPConfigurator(getInjector())); + builder.configurator(new GLSPConfigurator(createInjector())); container.addEndpoint(builder.build()); // Start the server diff --git a/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF b/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF index ecf3da99..1cd0c668 100644 --- a/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF @@ -12,7 +12,8 @@ Require-Bundle: org.eclipse.glsp.api;bundle-version="0.7.0";visibility:=reexport com.google.gson;bundle-version="2.8.2", org.eclipse.lsp4j;bundle-version="0.7.0", org.eclipse.lsp4j.jsonrpc, - com.google.guava;bundle-version="21.0.0" + com.google.guava;bundle-version="21.0.0", + org.apache.commons.cli;bundle-version="1.4.0";visibility:=reexport Import-Package: com.google.inject.multibindings;version="1.3.0" Export-Package: org.eclipse.glsp.server.actionhandler, org.eclipse.glsp.server.command, diff --git a/plugins/org.eclipse.glsp.server/pom.xml b/plugins/org.eclipse.glsp.server/pom.xml index 4f0a0ecf..be06e432 100644 --- a/plugins/org.eclipse.glsp.server/pom.xml +++ b/plugins/org.eclipse.glsp.server/pom.xml @@ -64,6 +64,11 @@ org.eclipse.emf.ecore.change 2.14.0 + + commons-cli + commons-cli + 1.4 + diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/CLIParser.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/CLIParser.java new file mode 100644 index 00000000..351f4d4e --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/CLIParser.java @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2020 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.launch; + +import java.util.function.Predicate; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.log4j.Logger; +import org.eclipse.glsp.server.utils.LaunchUtil; + +public abstract class CLIParser { + private static final Logger LOG = Logger.getLogger(CLIParser.class); + protected static final String INVALID_ARGUMENT_MESSAGE = "%s' is not a valid argument for option '--%s'! The default value '%s' is used."; + + protected final CommandLine cmd; + protected final Options options; + protected final String processName; + + public CLIParser(final String[] args, final Options options, final String processName) throws ParseException { + this.cmd = new DefaultParser().parse(options, args); + this.options = options; + this.processName = processName; + } + + public boolean hasOption(final String optionName) { + return cmd.hasOption(optionName); + } + + public String parseOption(final String optionName, final String defaultValue) { + return parseOption(optionName, defaultValue, null); + } + + public String parseOption(final String optionName, final String defaultValue, final Predicate validator) { + String arg = cmd.getOptionValue(optionName); + if (arg != null) { + if (validator == null || validator.test(arg)) { + return arg; + } + LOG.warn(String.format(INVALID_ARGUMENT_MESSAGE, + arg, optionName, defaultValue)); + } + return defaultValue; + } + + public int parseIntOption(final String optionName, final int defaultValue) { + return parseIntOption(optionName, defaultValue, null); + } + + public int parseIntOption(final String optionName, final int defaultValue, final Predicate validator) { + String intArg = cmd.getOptionValue(optionName); + int value = defaultValue; + if (intArg != null) { + try { + value = Integer.parseInt(intArg); + if (validator != null && !validator.test(value)) { + throw new NumberFormatException(); + } + } catch (NumberFormatException e) { + LOG.warn(String.format(INVALID_ARGUMENT_MESSAGE, + intArg, optionName, defaultValue)); + } + } + return value; + } + + public boolean parseBoolOption(final String optionName, final boolean defaultValue) { + String arg = cmd.getOptionValue(optionName); + return arg != null ? Boolean.parseBoolean(arg) : defaultValue; + } + + public void printHelp() { + LaunchUtil.printHelp(this.processName, options); + } + + public CommandLine getCmd() { return cmd; } +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultCLIParser.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultCLIParser.java new file mode 100644 index 00000000..fbd4e352 --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultCLIParser.java @@ -0,0 +1,85 @@ +/******************************************************************************** + * Copyright (c) 2020 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.launch; + +import java.io.File; +import java.util.function.Predicate; + +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.log4j.Level; +import org.eclipse.glsp.server.utils.LaunchUtil; +import org.eclipse.glsp.server.utils.LaunchUtil.DefaultOptions; + +public class DefaultCLIParser extends CLIParser { + public static final String OPTION_HELP = "help"; + public static final String OPTION_PORT = "port"; + public static final String OPTION_CONSOLE_LOG = "consoleLog"; + public static final String OPTION_FILE_LOG = "fileLog"; + public static final String OPTION_LOG_LEVEL = "logLevel"; + public static final String OPTION_LOG_DIR = "logDir"; + + public DefaultCLIParser(final String[] args, final String processName) throws ParseException { + this(args, getDefaultOptions(), processName); + } + + public DefaultCLIParser(final String[] args, final Options options, final String processName) throws ParseException { + super(args, options, processName); + } + + public int parsePort() { + Predicate validator = (port) -> LaunchUtil.isValidPort(port); + return parseIntOption(OPTION_PORT, DefaultOptions.SERVER_PORT, validator); + } + + public String parseLogDir() { + Predicate validator = (logDirArg) -> { + File file = new File(logDirArg); + return file.exists() && file.isDirectory(); + }; + String logDir = parseOption(OPTION_LOG_DIR, DefaultOptions.LOG_DIR, validator); + return new File(logDir).getAbsolutePath(); + } + + public Level parseLogLevel() { + String levelArg = parseOption(OPTION_LOG_LEVEL, DefaultOptions.LOG_LEVEL.toString()); + return Level.toLevel(levelArg, DefaultOptions.LOG_LEVEL); + } + + public boolean isConsoleLog() { return parseBoolOption(OPTION_CONSOLE_LOG, DefaultOptions.CONSOLE_LOG_ENABLED); } + + public boolean isFileLog() { return parseBoolOption(OPTION_FILE_LOG, DefaultOptions.FILE_LOG_ENABLED); } + + public boolean isHelp() { return hasOption(OPTION_HELP); } + + public static Options getDefaultOptions() { + Options options = new Options(); + options.addOption(null, OPTION_HELP, false, "Display usage information about GLSPServerLauncher"); + options.addOption(null, OPTION_PORT, true, + String.format("Set server port. [default='%s']", DefaultOptions.SERVER_PORT)); + options.addOption(null, OPTION_CONSOLE_LOG, true, + String.format("Enable/Disable console logging. [default='%s']", DefaultOptions.CONSOLE_LOG_ENABLED)); + options.addOption(null, OPTION_FILE_LOG, true, + String.format("Enable/Disable file logging. [default='%s']", DefaultOptions.FILE_LOG_ENABLED)); + options.addOption(null, OPTION_LOG_DIR, true, + String.format("Set the directory for log files (File logging has to be enabled). [default='%s']", + DefaultOptions.LOG_DIR)); + options.addOption(null, OPTION_LOG_LEVEL, true, + String.format("Set the log level. [default='%s']", DefaultOptions.LOG_LEVEL)); + return options; + } + +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultGLSPServerLauncher.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultGLSPServerLauncher.java index bafd4c24..64988339 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultGLSPServerLauncher.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/DefaultGLSPServerLauncher.java @@ -40,7 +40,6 @@ import org.eclipse.lsp4j.jsonrpc.MessageConsumer; import com.google.gson.GsonBuilder; -import com.google.inject.Guice; import com.google.inject.Injector; public class DefaultGLSPServerLauncher extends GLSPServerLauncher { @@ -50,22 +49,20 @@ public class DefaultGLSPServerLauncher extends GLSPServerLauncher { private AsynchronousServerSocketChannel serverSocket; private CompletableFuture onShutdown; - public DefaultGLSPServerLauncher(final GLSPModule module) { - super(module); + public DefaultGLSPServerLauncher(final GLSPModule glspModule) { + super(glspModule); } @Override - public void run(final String hostname, final int port) { + public void start(final String hostname, final int port) { Future onClose; try { onClose = asyncRun(hostname, port); onClose.get(); log.info("Stopped GLSP server"); } catch (IOException | InterruptedException | ExecutionException e) { - log.error(e.getMessage()); - e.printStackTrace(); + log.error("Error during server close!", e); } - } public Future asyncRun(final String hostname, final int port) @@ -89,13 +86,13 @@ public void failed(final Throwable exc, final Void attachment) { }; serverSocket.accept(null, handler); - log.info("The GLSP server is ready to accept new client requests"); + log.info("The GLSP server is ready to accept new client requests on port: " + port); return onShutdown; } private void createClientConnection(final AsynchronousSocketChannel socketChannel) { - Injector injector = Guice.createInjector(getGLSPModule()); + Injector injector = createInjector(); GsonConfigurator gsonConf = injector.getInstance(GsonConfigurator.class); try { InputStream in = Channels.newInputStream(socketChannel); @@ -117,7 +114,7 @@ private void createClientConnection(final AsynchronousSocketChannel socketChanne try { socketChannel.close(); } catch (IOException e) { - log.debug("Excpetion occured when trying to close socketChannel", e); + log.error("Excpetion occured when trying to close socketChannel", e); } } } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/GLSPServerLauncher.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/GLSPServerLauncher.java index 68378971..b7b5e628 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/GLSPServerLauncher.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/launch/GLSPServerLauncher.java @@ -15,40 +15,40 @@ ********************************************************************************/ package org.eclipse.glsp.server.launch; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.eclipse.glsp.api.di.GLSPModule; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.Module; public abstract class GLSPServerLauncher { - private GLSPModule module; - private Injector injector; - - public GLSPServerLauncher() {} + private final GLSPModule glspModule; + private final List modules; - public GLSPServerLauncher(final GLSPModule module) { - this.module = module; + public GLSPServerLauncher(final GLSPModule glspModule) { + this.glspModule = glspModule; + modules = new ArrayList<>(); + modules.add(glspModule); } - protected Injector doSetup() { - return Guice.createInjector(module); + public void addAdditionalModules(final Module... modules) { + Arrays.stream(modules) + .filter(module -> !this.modules.contains(module)) + .forEach(this.modules::add); } - public void start(final String hostname, final int port) { - if (injector == null) { - injector = doSetup(); - } - run(hostname, port); + public Injector createInjector() { + return Guice.createInjector(modules); } - protected abstract void run(String hostname, int port); + public abstract void start(String hostname, int port); public abstract void shutdown(); - public GLSPModule getGLSPModule() { return module; } - - public Injector getInjector() { return injector; } - - public void setModule(final GLSPModule module) { this.module = module; } + public GLSPModule getGLSPModule() { return glspModule; } } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/LaunchUtil.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/LaunchUtil.java new file mode 100644 index 00000000..1761ba6a --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/LaunchUtil.java @@ -0,0 +1,115 @@ +/******************************************************************************** + * Copyright (c) 2020 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.utils; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.log4j.Appender; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.FileAppender; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.PatternLayout; +import org.eclipse.glsp.server.launch.DefaultCLIParser; + +public final class LaunchUtil { + private static Logger LOG = Logger.getLogger(LaunchUtil.class); + + private LaunchUtil() {} + + public static final class DefaultOptions { + public static final int SERVER_PORT = 5007; + public static final Level LOG_LEVEL = Level.INFO; + public static final String LOG_DIR = new File("./logs/").getAbsolutePath(); + public static final boolean CONSOLE_LOG_ENABLED = true; + public static final boolean FILE_LOG_ENABLED = false; + } + + public static boolean isValidPort(final Integer port) { + return port >= 0 && port <= 65535; + } + + public static void configure(final DefaultCLIParser cli) throws ParseException, IOException { + if (cli.isHelp()) { + cli.printHelp(); + System.exit(0); + } + configureLogger(cli); + if (cli.hasOption(DefaultCLIParser.OPTION_LOG_DIR) && !cli.isFileLog()) { + LOG.warn(String.format("File logging is disabled. The option '--%s' will be ignored.", + DefaultCLIParser.OPTION_LOG_DIR)); + } + } + + public static void configureLogger(final DefaultCLIParser cli) throws ParseException, IOException { + if (cli.isFileLog()) { + configureLogger(cli.isConsoleLog(), cli.parseLogDir(), cli.parseLogLevel()); + } else { + configureLogger(cli.isConsoleLog(), cli.parseLogLevel()); + } + } + + public static void configureLogger(final boolean logToConsole, final Level logLevel) throws IOException { + configureLogger(logToConsole, null, logLevel); + } + + public static void configureLogger(final boolean logToConsole, final String logDir, final Level logLevel) + throws IOException { + Logger root = Logger.getRootLogger(); + List consoleAppenders = getAppenders(root, ConsoleAppender.class); + if (logToConsole) { + if (consoleAppenders.isEmpty()) { + root.addAppender(new ConsoleAppender( + new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); + } + } else { + // Remove all console log appenders + consoleAppenders.forEach(root::removeAppender); + } + if (logDir != null && !logDir.isEmpty()) { + SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy_HH:mm:ss"); + String fileName = formatter.format(new Date()) + ".log"; + String logFile = new File(logDir, fileName).getAbsolutePath(); + root.addAppender( + new FileAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN), logFile)); + } + root.setLevel(logLevel); + } + + @SuppressWarnings("unchecked") + public static List getAppenders(final Logger logger, final Class clazz) { + List result = new ArrayList<>(); + logger.getAllAppenders().asIterator().forEachRemaining((appender) -> { + if (clazz.isInstance(appender)) { + result.add(clazz.cast(appender)); + } + }); + return result; + } + + public static void printHelp(final String processName, final Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(90, processName, "\noptions:", options, "", true); + } +} diff --git a/targetplatforms/r2020-06.target b/targetplatforms/r2020-06.target index c8e099f0..2df077d8 100644 --- a/targetplatforms/r2020-06.target +++ b/targetplatforms/r2020-06.target @@ -1,7 +1,7 @@ - + @@ -11,6 +11,7 @@ + diff --git a/targetplatforms/r2020-06.tpd b/targetplatforms/r2020-06.tpd index bfdd3b04..b95a1aec 100644 --- a/targetplatforms/r2020-06.tpd +++ b/targetplatforms/r2020-06.tpd @@ -8,6 +8,7 @@ location "http://download.eclipse.org/releases/2020-06" { location "https://download.eclipse.org/tools/orbit/downloads/drops/R20200529191137/repository/" { org.apache.log4j + org.apache.commons.cli org.apache.commons.io javax.servlet com.google.gson [2.8.2,3.0.0)