| /* |
| * 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.ArrayList; |
| import java.util.Collection; |
| import java.util.Map; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.atomic.AtomicInteger; |
| 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.configuration.IgniteConfiguration; |
| import org.apache.ignite.internal.util.typedef.G; |
| import org.apache.ignite.testframework.GridTestUtils; |
| import org.springframework.beans.BeansException; |
| import org.springframework.context.ApplicationContext; |
| import org.springframework.context.support.FileSystemXmlApplicationContext; |
| |
| import static org.apache.ignite.IgniteSystemProperties.IGNITE_UPDATE_NOTIFIER; |
| |
| /** |
| * This class |
| * |
| */ |
| public final class GridVmNodesStarter { |
| /** Name of the system property defining name of command line program. */ |
| private static final String IGNITE_PROG_NAME = "IGNITE_PROG_NAME"; |
| |
| /** */ |
| private static final String IGNITE_INSTANCE_NAME_PREF = "gg-vm-grid-"; |
| |
| /** */ |
| private static final int DFLT_NODES_COUNT = 20; |
| |
| /** */ |
| private static final String OPTION_CFG = "cfg"; |
| |
| /** */ |
| private static final String OPTION_N = "n"; |
| |
| /** */ |
| private static final AtomicInteger gridCnt = new AtomicInteger(); |
| |
| /** |
| * Enforces singleton. |
| */ |
| private GridVmNodesStarter() { |
| // 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(String errMsg, 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); |
| } |
| |
| /** |
| * Main entry point. |
| * |
| * @param args Command line arguments. |
| * @throws IgniteCheckedException If failed. |
| */ |
| public static void main(String[] args) throws IgniteCheckedException { |
| System.setProperty(IGNITE_UPDATE_NOTIFIER, "false"); |
| |
| Options options = createOptions(); |
| |
| // Create the command line parser. |
| CommandLineParser parser = new PosixParser(); |
| |
| String cfgPath = null; |
| |
| Integer nodesCnt = null; |
| |
| try { |
| CommandLine cmd = parser.parse(options, args); |
| |
| if (cmd.hasOption(OPTION_CFG)) |
| cfgPath = cmd.getOptionValue(OPTION_CFG); |
| |
| if (cmd.hasOption(OPTION_N)) |
| try { |
| nodesCnt = Integer.parseInt(cmd.getOptionValue(OPTION_N)); |
| } |
| catch (NumberFormatException ignored) { |
| // No-op. |
| } |
| |
| if (nodesCnt == null) |
| nodesCnt = DFLT_NODES_COUNT; |
| } |
| catch (ParseException e) { |
| exit(e.getMessage(), options, -1); |
| } |
| |
| System.out.println(); |
| System.out.println(">>> VM Nodes Starter parameters:"); |
| System.out.println(" Nodes Count: " + nodesCnt); |
| System.out.println(" Config Path: " + cfgPath); |
| System.out.println(); |
| |
| final IgniteConfiguration[] cfgs = new IgniteConfiguration[nodesCnt]; |
| |
| for (int i = 0; i < nodesCnt; i++) |
| cfgs[i] = getConfigurations(cfgPath).iterator().next(); |
| |
| final AtomicInteger cfgIdx = new AtomicInteger(0); |
| |
| GridTestUtils.runMultiThreadedAsync(new Callable<Object>() { |
| @Override public Object call() throws Exception { |
| G.start(cfgs[cfgIdx.getAndIncrement()]); |
| |
| return null; |
| } |
| }, nodesCnt, "test-node-starter"); |
| } |
| |
| /** |
| * Initializes configurations. |
| * |
| * |
| * @param springCfgPath Configuration file path. |
| * @return List of configurations. |
| * @throws IgniteCheckedException If an error occurs. |
| */ |
| private static Iterable<IgniteConfiguration> getConfigurations(String springCfgPath) |
| throws IgniteCheckedException { |
| 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.isEmpty()) |
| throw new IgniteCheckedException("Can't find grid factory configuration in: " + path); |
| |
| Collection<IgniteConfiguration> res = new ArrayList<>(); |
| |
| for (IgniteConfiguration cfg : (Collection<IgniteConfiguration>)cfgMap.values()) { |
| res.add(cfg); |
| |
| cfg.setIgniteInstanceName(IGNITE_INSTANCE_NAME_PREF + gridCnt.incrementAndGet()); |
| } |
| |
| return res; |
| } |
| |
| /** |
| * Creates cli options. |
| * |
| * @return Command line options |
| */ |
| private static Options createOptions() { |
| Options options = new Options(); |
| |
| OptionGroup grp = new OptionGroup(); |
| |
| grp.setRequired(true); |
| |
| Option cfg = new Option(OPTION_CFG, null, true, "path to Spring XML configuration file."); |
| |
| cfg.setArgName("file"); |
| |
| Option n = new Option(null, OPTION_N, true, "nodes count."); |
| |
| n.setValueSeparator('='); |
| n.setType(Integer.class); |
| |
| grp.addOption(cfg); |
| grp.addOption(n); |
| |
| options.addOptionGroup(grp); |
| |
| return options; |
| } |
| } |