/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ngrinder; import com.beust.jcommander.*; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.webapp.WebAppContext; import java.io.File; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.net.InetSocketAddress; import java.net.Socket; import java.security.ProtectionDomain; import java.util.HashMap; import java.util.List; import java.util.Map; @SuppressWarnings("FieldCanBeLocal") @Parameters(separators = "= ") public class NGrinderControllerStarter { @Parameters(separators = "= ") enum ClusterMode { none { @Parameter(names = {"-cp", "--controller-port"}, description = "controller port for agent connection", validateValueWith = PortAvailabilityValidator.class) public Integer controllerPort = 16001; public void process() { if (controllerPort != null) { System.setProperty("controller.controller_port", controllerPort.toString()); } } }, easy { @Parameter(names = {"-clh", "--cluster-host"}, required = false, description = "This cluster member's cluster communication host. The default value is the " + "first non-localhost address. if it's localhost, " + "it can only communicate with the other cluster members in the same machine.") private String clusterHost = null; @Parameter(names = {"-clp", "--cluster-port"}, required = true, description = "This cluster member's cluster communication port. Each cluster member should " + "be run with unique cluster port.", validateValueWith = PortAvailabilityValidator.class) private Integer clusterPort = null; @Parameter(names = {"-cp", "--controller-port"}, required = true, description = "This cluster member's agent connection port", validateValueWith = PortAvailabilityValidator.class) private Integer controllerPort = null; @Parameter(names = {"-r", "--region"}, required = true, description = "This cluster member's region name") private String region = null; @Parameter(names = {"-dh", "--database-host"}, required = false, description = "database host. The default value is localhost") private String databaseHost = "localhost"; @Parameter(names = {"-dp", "--database-port"}, required = false, description = "database port. The default value is 9092 when h2 is used and " + "33000 when cubrid is used." ) private Integer databasePort = null; @Parameter(names = {"-dt", "--database-type"}, required = false, description = "database type", hidden = true) private String databaseType = "h2"; @SuppressWarnings("SpellCheckingInspection") public void process() { System.setProperty("cluster.mode", "easy"); if (clusterHost != null) { System.setProperty("cluster.host", clusterHost); } System.setProperty("cluster.port", clusterPort.toString()); System.setProperty("cluster.region", region); System.setProperty("controller.controller_port", controllerPort.toString()); System.setProperty("database.type", databaseType); if ("h2".equals(databaseType)) { if (databasePort == null) { databasePort = 9092; } if (!tryConnection(databaseHost, databasePort)) { throw new ParameterException("Failed to connect h2 db " + databaseHost + ":" + databasePort + ".\nPlease run the h2 TcpServer in advance\n" + "or set the correct -database-host and -database-port parameters"); } System.setProperty("database.url", "tcp://" + this.databaseHost + ":" + databasePort + "/db/ngrinder"); } else { if (databasePort == null) { databasePort = 33000; } if (!tryConnection(databaseHost, databasePort)) { throw new ParameterException("Failed to connect cubrid db.\n" + "Please run the cubrid db " + databaseHost + ":" + databasePort + "in advance\n" + "or set the correct -database-host and -database-port parameters"); } System.setProperty("database.url", this.databaseHost + ":" + this.databasePort); } } }, advanced { public void process() { System.setProperty("cluster.mode", "advanced"); } }; public void parseArgs(String[] args) { JCommander commander = new JCommander(ClusterMode.this); String clusterModeOption = ""; if (this != ClusterMode.none) { clusterModeOption = " --cluster-mode=" + name(); } commander.setProgramName(getRunningCommand() + clusterModeOption); try { commander.parse(args); process(); } catch (Exception e) { System.err.println("[Configuration Error]"); System.err.println(e.getMessage()); commander.usage(); System.exit(-1); } } abstract void process(); } public static boolean tryConnection(String byConnecting, int port) { Socket socket = null; try { socket = new Socket(); socket.connect(new InetSocketAddress(byConnecting, port), 2000); // 2 seconds timeout } catch (Exception e) { return false; } finally { if (socket != null) { try { socket.close(); } catch (Exception e) { // } } } return true; } private static final String NGRINDER_DEFAULT_FOLDER = ".ngrinder"; @Parameter(names = {"-p", "--port"}, description = "HTTP port of the server. The default port is 8080.", validateValueWith = PortAvailabilityValidator.class) private Integer port = null; @Parameter(names = {"-c", "--context-path"}, description = "context path of the embedded web application.") private String contextPath = "/"; @Parameter(names = {"-cm", "--cluster-mode"}, description = "cluster-mode can be easy or advanced ") private String clusterMode = "none"; @Parameter(names = {"-nh", "--ngrinder-home"}, description = "nGridner home directory") private String home = null; @SuppressWarnings("SpellCheckingInspection") @Parameter(names = {"-exh", "--ex-home"}, description = "nGridner extended home directory") private String exHome = null; @Parameter(names = {"-help", "-?", "-h"}, description = "prints this message") private Boolean help = false; @DynamicParameter(names = "-D", description = "Dynamic parameters", hidden = true) private Map<String, String> params = new HashMap<String, String>(); public static boolean isEmpty(String str) { return str == null || str.length() == 0; } public static String defaultIfEmpty(String str, String defaultStr) { return isEmpty(str) ? defaultStr : str; } public File resolveHome() { String userHomeFromEnv = System.getenv("NGRINDER_HOME"); String userHomeFromProperty = System.getProperty("ngrinder.home"); String userHome = defaultIfEmpty(userHomeFromProperty, userHomeFromEnv); return (!isEmpty(userHome)) ? new File(userHome) : new File( System.getProperty("user.home"), NGRINDER_DEFAULT_FOLDER); } private void run() { Server server = new Server(port); ServerConnector connector = new ServerConnector(server); // Set some timeout options to make debugging easier. connector.setIdleTimeout(1000 * 60 * 60); connector.setSoLingerTime(-1); connector.setPort(port); server.setConnectors(new Connector[]{connector}); WebAppContext context = new WebAppContext(); final File tmpDir = new File(resolveHome(), "tmp"); //noinspection ResultOfMethodCallIgnored tmpDir.mkdirs(); context.setTempDirectory(tmpDir); context.setServer(server); if (!contextPath.startsWith("/")) { contextPath = "/" + contextPath; } context.setContextPath(contextPath); String war = getWarName(); context.setWar(war); server.setHandler(context); try { server.start(); //noinspection StatementWithEmptyBody while (System.in.read() != 'q') { Thread.sleep(1000); } server.stop(); server.join(); } catch (Exception ex) { ex.printStackTrace(); System.exit(-1); } } private static String getWarName() { ProtectionDomain protectionDomain = NGrinderControllerStarter.class.getProtectionDomain(); String warName = protectionDomain.getCodeSource().getLocation().toExternalForm(); if (warName.endsWith("/classes/")) { warName = "ngrinder-controller-X.X.war"; } return warName; } private static long getMaxPermGen() { for (MemoryPoolMXBean each : ManagementFactory.getMemoryPoolMXBeans()) { if (each.getName().endsWith("Perm Gen")) { return each.getUsage().getMax(); } } return Long.MAX_VALUE; } public static void main(String[] args) throws Exception { if (System.getProperty("unit-test") == null && getMaxPermGen() < (1024 * 1024 * 200)) { System.out.println( "nGrinder needs quite big perm-gen memory.\n" + "Please run nGrinder with the following command.\n" + getRunningCommand()); System.exit(-1); } NGrinderControllerStarter server = new NGrinderControllerStarter(); JCommander commander = new JCommander(server); commander.setAcceptUnknownOptions(true); commander.setProgramName("ngrinder"); PortAvailabilityValidator validator = new PortAvailabilityValidator(); try { commander.parse(args); if (server.port == null) { server.port = 8080; } validator.validate("-p / --port", server.port); } catch (Exception e) { System.err.println(e.getMessage()); commander.usage(); System.exit(0); } if (server.help) { commander.usage(); System.exit(0); } if (server.home != null) { System.setProperty("ngrinder.home", server.home); } if (server.exHome != null) { System.setProperty("ngrinder.ex.home", server.exHome); } final List<String> unknownOptions = commander.getUnknownOptions(); final ClusterMode clusterMode = ClusterMode.valueOf(server.clusterMode); clusterMode.parseArgs(unknownOptions.toArray(new String[unknownOptions.size()])); System.getProperties().putAll(server.params); server.run(); } private static String getRunningCommand() { return "java -XX:MaxPermSize=200m -jar " + new File(getWarName()).getName(); } }