| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.ignite.startup; |
| |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.concurrent.CountDownLatch; |
| import org.apache.commons.cli.CommandLine; |
| import org.apache.commons.cli.CommandLineParser; |
| import org.apache.commons.cli.HelpFormatter; |
| import org.apache.commons.cli.Option; |
| import org.apache.commons.cli.OptionGroup; |
| import org.apache.commons.cli.Options; |
| import org.apache.commons.cli.ParseException; |
| import org.apache.commons.cli.PosixParser; |
| import org.apache.ignite.IgniteCheckedException; |
| import org.apache.ignite.IgniteState; |
| import org.apache.ignite.IgniteSystemProperties; |
| import org.apache.ignite.IgnitionListener; |
| import org.apache.ignite.configuration.IgniteConfiguration; |
| import org.apache.ignite.internal.IgniteVersionUtils; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.apache.ignite.internal.util.typedef.G; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.testframework.GridTestUtils; |
| import org.apache.ignite.testframework.junits.logger.GridTestLog4jLogger; |
| import org.jetbrains.annotations.Nullable; |
| import org.springframework.beans.BeansException; |
| import org.springframework.context.ApplicationContext; |
| import org.springframework.context.support.FileSystemXmlApplicationContext; |
| |
| import static org.apache.ignite.IgniteState.STOPPED; |
| |
| /** |
| * This class defines random command-line Ignite loader. This loader can be used |
| * to randomly start and stop Ignite from command line for tests. This loader is a Java |
| * application with {@link #main(String[])} method that accepts command line arguments. |
| * See below for details. |
| */ |
| public final class GridRandomCommandLineLoader { |
| /** Name of the system property defining name of command line program. */ |
| private static final String IGNITE_PROG_NAME = "IGNITE_PROG_NAME"; |
| |
| /** Copyright text. Ant processed. */ |
| private static final String COPYRIGHT = IgniteVersionUtils.COPYRIGHT; |
| |
| /** Version. Ant processed. */ |
| private static final String VER = "x.x.x"; |
| |
| /** */ |
| private static final String OPTION_HELP = "help"; |
| |
| /** */ |
| private static final String OPTION_CFG = "cfg"; |
| |
| /** */ |
| private static final String OPTION_MIN_TTL = "minTtl"; |
| |
| /** */ |
| private static final String OPTION_MAX_TTL = "maxTtl"; |
| |
| /** */ |
| private static final String OPTION_DURATION = "duration"; |
| |
| /** */ |
| private static final String OPTION_LOG_CFG = "logCfg"; |
| |
| /** Minimal value for timeout in milliseconds. */ |
| private static final long DFLT_MIN_TIMEOUT = 1000; |
| |
| /** Maximum value for timeout in milliseconds. */ |
| private static final long DFLT_MAX_TIMEOUT = 1000 * 20; |
| |
| /** Work timeout in milliseconds. */ |
| private static final long DFLT_RUN_TIMEOUT = 1000 * 60 * 5; |
| |
| /** Latch. */ |
| private static CountDownLatch latch; |
| |
| /** |
| * Enforces singleton. |
| */ |
| private GridRandomCommandLineLoader() { |
| // No-op. |
| } |
| |
| /** |
| * Echos the given messages. |
| * |
| * @param msg Message to echo. |
| */ |
| private static void echo(String msg) { |
| assert msg != null; |
| |
| System.out.println(msg); |
| } |
| |
| /** |
| * Echos exception stack trace. |
| * |
| * @param e Exception to print. |
| */ |
| private static void echo(IgniteCheckedException e) { |
| assert e != null; |
| |
| System.err.println(e); |
| } |
| |
| /** |
| * Exists with optional error message, usage show and exit code. |
| * |
| * @param errMsg Optional error message. |
| * @param options Command line options to show usage information. |
| * @param exitCode Exit code. |
| */ |
| private static void exit(@Nullable String errMsg, @Nullable Options options, int exitCode) { |
| if (errMsg != null) |
| echo("ERROR: " + errMsg); |
| |
| String runner = System.getProperty(IGNITE_PROG_NAME, "randignite.{sh|bat}"); |
| |
| int space = runner.indexOf(' '); |
| |
| runner = runner.substring(0, space == -1 ? runner.length() : space); |
| |
| if (options != null) { |
| HelpFormatter formatter = new HelpFormatter(); |
| |
| formatter.printHelp(runner, options); |
| } |
| |
| System.exit(exitCode); |
| } |
| |
| /** |
| * Prints logo. |
| */ |
| private static void logo() { |
| echo("Ignite Random Command Line Loader, ver. " + VER); |
| echo(COPYRIGHT); |
| echo(""); |
| } |
| |
| /** |
| * Main entry point. |
| * |
| * @param args Command line arguments. |
| */ |
| @SuppressWarnings({"BusyWait"}) |
| public static void main(String[] args) { |
| System.setProperty(IgniteSystemProperties.IGNITE_UPDATE_NOTIFIER, "false"); |
| |
| logo(); |
| |
| Options options = createOptions(); |
| |
| // Create the command line parser. |
| CommandLineParser parser = new PosixParser(); |
| |
| String cfgPath = null; |
| |
| long minTtl = DFLT_MIN_TIMEOUT; |
| long maxTtl = DFLT_MAX_TIMEOUT; |
| long duration = DFLT_RUN_TIMEOUT; |
| |
| String logCfgPath = null; |
| |
| try { |
| CommandLine cmd = parser.parse(options, args); |
| |
| if (cmd.hasOption(OPTION_HELP)) |
| exit(null, options, 0); |
| |
| if (!cmd.hasOption(OPTION_LOG_CFG)) |
| exit("-log should be set", options, -1); |
| else |
| logCfgPath = cmd.getOptionValue(OPTION_LOG_CFG); |
| |
| if (cmd.hasOption(OPTION_CFG)) |
| cfgPath = cmd.getOptionValue(OPTION_CFG); |
| |
| try { |
| if (cmd.hasOption(OPTION_DURATION)) |
| duration = Long.parseLong(cmd.getOptionValue(OPTION_DURATION)); |
| } |
| catch (NumberFormatException ignored) { |
| exit("Invalid argument for option: " + OPTION_DURATION, options, -1); |
| } |
| |
| try { |
| if (cmd.hasOption(OPTION_MIN_TTL)) |
| minTtl = Long.parseLong(cmd.getOptionValue(OPTION_MIN_TTL)); |
| } |
| catch (NumberFormatException ignored) { |
| exit("Invalid argument for option: " + OPTION_MIN_TTL, options, -1); |
| } |
| |
| try { |
| if (cmd.hasOption(OPTION_MAX_TTL)) |
| maxTtl = Long.parseLong(cmd.getOptionValue(OPTION_MAX_TTL)); |
| } |
| catch (NumberFormatException ignored) { |
| exit("Invalid argument for option: " + OPTION_MAX_TTL, options, -1); |
| } |
| |
| if (minTtl >= maxTtl) |
| exit("Invalid arguments for options: " + OPTION_MAX_TTL + ", " + OPTION_MIN_TTL, options, -1); |
| } |
| catch (ParseException e) { |
| exit(e.getMessage(), options, -1); |
| } |
| |
| System.out.println("Configuration path: " + cfgPath); |
| System.out.println("Log4j configuration path: " + logCfgPath); |
| System.out.println("Duration: " + duration); |
| System.out.println("Minimum TTL: " + minTtl); |
| System.out.println("Maximum TTL: " + maxTtl); |
| |
| G.addListener(new IgnitionListener() { |
| @Override public void onStateChange(String name, IgniteState state) { |
| if (state == STOPPED && latch != null) |
| latch.countDown(); |
| } |
| }); |
| |
| Random rand = new Random(); |
| |
| long now = System.currentTimeMillis(); |
| |
| long end = duration + System.currentTimeMillis(); |
| |
| try { |
| while (now < end) { |
| G.start(getConfiguration(cfgPath, logCfgPath)); |
| |
| long delay = rand.nextInt((int)(maxTtl - minTtl)) + minTtl; |
| |
| delay = (now + delay > end) ? (end - now) : delay; |
| |
| now = System.currentTimeMillis(); |
| |
| echo("Time left (ms): " + (end - now)); |
| |
| echo("Going to sleep for (ms): " + delay); |
| |
| Thread.sleep(delay); |
| |
| G.stopAll(false); |
| |
| now = System.currentTimeMillis(); |
| } |
| } |
| catch (IgniteCheckedException e) { |
| echo(e); |
| |
| exit("Failed to start grid: " + e.getMessage(), null, -1); |
| } |
| catch (InterruptedException e) { |
| echo("Loader was interrupted (exiting): " + e.getMessage()); |
| } |
| |
| latch = new CountDownLatch(G.allGrids().size()); |
| |
| try { |
| while (latch.getCount() > 0) |
| latch.await(); |
| } |
| catch (InterruptedException e) { |
| echo("Loader was interrupted (exiting): " + e.getMessage()); |
| } |
| |
| System.exit(0); |
| } |
| |
| /** |
| * Initializes configurations. |
| * |
| * @param springCfgPath Configuration file path. |
| * @param logCfgPath Log file name. |
| * @return List of configurations. |
| * @throws IgniteCheckedException If an error occurs. |
| */ |
| private static IgniteConfiguration getConfiguration(String springCfgPath, @Nullable String logCfgPath) |
| throws IgniteCheckedException { |
| assert springCfgPath != null; |
| |
| File path = GridTestUtils.resolveIgnitePath(springCfgPath); |
| |
| if (path == null) |
| throw new IgniteCheckedException("Spring XML configuration file path is invalid: " + new File(springCfgPath) + |
| ". Note that this path should be either absolute path or a relative path to IGNITE_HOME."); |
| |
| if (!path.isFile()) |
| throw new IgniteCheckedException("Provided file path is not a file: " + path); |
| |
| ApplicationContext springCtx; |
| |
| try { |
| springCtx = new FileSystemXmlApplicationContext(path.toURI().toURL().toString()); |
| } |
| catch (BeansException | MalformedURLException e) { |
| throw new IgniteCheckedException("Failed to instantiate Spring XML application context: " + e.getMessage(), e); |
| } |
| |
| Map cfgMap; |
| |
| try { |
| // Note: Spring is not generics-friendly. |
| cfgMap = springCtx.getBeansOfType(IgniteConfiguration.class); |
| } |
| catch (BeansException e) { |
| throw new IgniteCheckedException("Failed to instantiate bean [type=" + IgniteConfiguration.class + ", err=" + |
| e.getMessage() + ']', e); |
| } |
| |
| if (cfgMap == null) |
| throw new IgniteCheckedException("Failed to find a single grid factory configuration in: " + path); |
| |
| if (cfgMap.size() != 1) |
| throw new IgniteCheckedException("Spring configuration file should contain exactly 1 grid configuration: " + path); |
| |
| IgniteConfiguration cfg = (IgniteConfiguration)F.first(cfgMap.values()); |
| |
| assert cfg != null; |
| |
| if (logCfgPath != null) |
| cfg.setGridLogger(new GridTestLog4jLogger(U.resolveIgniteUrl(logCfgPath))); |
| |
| return cfg; |
| } |
| |
| /** |
| * Creates cli options. |
| * |
| * @return Command line options |
| */ |
| private static Options createOptions() { |
| Options options = new Options(); |
| |
| Option help = new Option(OPTION_HELP, "print this message"); |
| |
| Option cfg = new Option(null, OPTION_CFG, true, "path to Spring XML configuration file."); |
| |
| cfg.setValueSeparator('='); |
| cfg.setType(String.class); |
| |
| Option minTtl = new Option(null, OPTION_MIN_TTL, true, "node minimum time to live."); |
| |
| minTtl.setValueSeparator('='); |
| minTtl.setType(Long.class); |
| |
| Option maxTtl = new Option(null, OPTION_MAX_TTL, true, "node maximum time to live."); |
| |
| maxTtl.setValueSeparator('='); |
| maxTtl.setType(Long.class); |
| |
| Option duration = new Option(null, OPTION_DURATION, true, "run timeout."); |
| |
| duration.setValueSeparator('='); |
| duration.setType(Long.class); |
| |
| Option log = new Option(null, OPTION_LOG_CFG, true, "path to log4j configuration file."); |
| |
| log.setValueSeparator('='); |
| log.setType(String.class); |
| |
| options.addOption(help); |
| |
| OptionGroup grp = new OptionGroup(); |
| |
| grp.setRequired(true); |
| |
| grp.addOption(cfg); |
| grp.addOption(minTtl); |
| grp.addOption(maxTtl); |
| grp.addOption(duration); |
| grp.addOption(log); |
| |
| options.addOptionGroup(grp); |
| |
| return options; |
| } |
| } |