blob: 550253bf76c261bb85bc87bbdfff6b1c26913613 [file] [log] [blame]
/*
* 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.cmdline;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* This class is a workaround for a versatile problems with passing arguments to the Ignite Windows batch launcher
* scripts. It transforms command line options passed to the *.bat launcher scripts into a reformatted, adapted for
* Windows batch usage variables.
* <p>
* The idea behind the workaround is:<br>
* 1. User runs ignite.bat with some args.<br>
* 2. ignite.bat calls parseargs.bat with all that args.<br>
* 3. parsearg.bat runs this class with all that args.<br>
* 4. This class transforms the args and prints them out.<br>
* 5. parseargs.bat splits the output of this class and sets each variable it found.<br>
* 6. ignite.bat uses environment variables as if they were defined by a user.
* <p>
*
* @see <a href="http://jcommander.org/">JCommander command line parameters parsing library</a>
*/
public class CommandLineTransformer {
/** Prefix for every custom JVM option. */
static final String JVM_OPTION_PREFIX = "-J";
/** Flag to check in step 5 of the Workaround before splitting the output. */
private static final String TRANSFORMATION_FAILED_FLAG = "CommandLineTransformerError";
/** Delimiter used in step 5 of the Workaround. */
private static final String ARGS_DELIMITER = " ";
/** Interactive mode. */
private boolean interactive;
/** Verbose mode. */
private boolean verbose;
/** No pause mode. */
private boolean noPause;
/** Supported parameter, parsed manually. */
private String jvmOptions = "";
/** Supported parameter, parsed manually. */
private String springCfgPath = "";
/**
* Private constructor to promote usage of a factory method {@link #transform(String...)}.
*/
private CommandLineTransformer() {
// No-op
}
/**
* Main method being triggered in step 3 of the Workaround.
* <p>
* Method prints out TRANSFORMATION_FAILED_FLAG if something went wrong.
*
* @param args Command line arguments passed by a user in step 1 of the Workaround.
*/
public static void main(String[] args) {
PrintStream ps = null;
try {
String encoding = System.getProperty("file.encoding", Charset.defaultCharset().name());
ps = new PrintStream(System.out, true, encoding);
ps.println(transform(args));
}
catch (Throwable t) {
t.printStackTrace();
if (ps != null)
ps.println(TRANSFORMATION_FAILED_FLAG);
if (t instanceof Error)
throw (Error)t;
}
}
/**
* @param args Arguments.
* @return Transformed arguments.
*/
public static String transform(String... args) {
assert args != null;
return new CommandLineTransformer().doTransformation(args);
}
/**
* Actually does arguments transformation.
*
* @param args Arguments.
* @return Transformed arguments.
*/
private String doTransformation(String[] args) {
List<String> argsList = new ArrayList<>();
for (String arg : args) {
switch (arg) {
case "-i":
interactive = true;
break;
case "-v":
verbose = true;
break;
case "-np":
noPause = true;
break;
default:
argsList.add(arg);
}
}
return reformatArguments(argsList);
}
/**
* Transforms parsed arguments into a string.
*
* @param args Non-standard arguments.
* @return Transformed arguments.
*/
private String reformatArguments(List<String> args) {
StringBuilder sb = new StringBuilder();
addArgWithValue(sb, "INTERACTIVE", formatBooleanValue(interactive));
addArgWithValue(sb, "QUIET", "-DIGNITE_QUIET=" + !verbose);
addArgWithValue(sb, "NO_PAUSE", formatBooleanValue(noPause));
parseJvmOptionsAndSpringConfig(args);
addArgWithValue(sb, "JVM_XOPTS", jvmOptions);
addArgWithValue(sb, "CONFIG", springCfgPath);
return sb.toString().trim();
}
/**
* Formats boolean value into a string one.
*
* @param val Boolean value to be formatted.
* @return 1 if val is true 0 otherwise.
*/
private String formatBooleanValue(boolean val) {
return String.valueOf(val ? 1 : 0);
}
/**
* Transforms one argument.
*
* @param sb StringBuilder where transformation result will be written.
* @param arg Argument's name.
* @param val Argument's value.
*/
private void addArgWithValue(StringBuilder sb, String arg, Object val) {
sb.append("\"");
sb.append(arg);
sb.append("=");
sb.append(val);
sb.append("\"");
sb.append(ARGS_DELIMITER);
}
/**
* Manually parses non-trivial (from JCommander point of view) arguments.
*
* @param args Collection of unknown (from JCommander point of view) arguments.
*/
private void parseJvmOptionsAndSpringConfig(Iterable<String> args) {
boolean hadFileEncoding = false;
for (String arg : args) {
if (arg.startsWith(JVM_OPTION_PREFIX)) {
String jvmOpt = arg.substring(JVM_OPTION_PREFIX.length());
if (jvmOpt.startsWith("-Dfile.encoding="))
hadFileEncoding = true;
if (!checkJVMOptionIsSupported(jvmOpt))
throw new RuntimeException(JVM_OPTION_PREFIX + " JVM parameters for Ignite batch scripts " +
"with double quotes are not supported. " +
"Use JVM_OPTS environment variable to pass any custom JVM option.");
jvmOptions = jvmOptions.isEmpty() ? jvmOpt : jvmOptions + " " + jvmOpt;
}
else {
if (springCfgPath.isEmpty())
springCfgPath = arg;
else
throw new RuntimeException("Unrecognised parameter has been found: " + arg);
}
}
if (!hadFileEncoding)
jvmOptions = (jvmOptions.isEmpty() ? "" : (jvmOptions + " ")) + "-Dfile.encoding=UTF-8";
}
/**
* Check if a JVM option is supported. Unsupported options are those who
* need double quotes in the declaration. It's a limitation of a current Workaround
*
* @param jvmOpt JVM option to check.
* @return true if option is supported false otherwise.
*/
private boolean checkJVMOptionIsSupported(String jvmOpt) {
return !(jvmOpt.contains("-XX:OnError") || jvmOpt.contains("-XX:OnOutOfMemoryError"));
}
}