org.glassfish.appclient.client.CLIBootstrap Maven / Gradle / Ivy
Show all versions of payara-micro Show documentation
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright 2018-2022 Payara Foundation and/or affiliates
package org.glassfish.appclient.client;
import com.sun.enterprise.util.LocalStringManager;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.OS;
import org.glassfish.appclient.client.acc.UserError;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.joining;
/**
*
* Constructs a java command to launch the ACC with the correct agent and command line arguments, based on the current
* operating environment and the user's own command-line arguments.
*
* The user might have specified JVM options as well as ACC options as
* well as arguments to be passed to the client. Further, we need to make
* sure that the GlassFish extension libraries directories
* directories are included
* regardless of whether the user specified any explicitly.
*
* This program emits a java command line that will run the ACC so that it will launch the client. The emitted command
* will need to look like this:
*
*
* {@code
* java \
* (user-specified JVM options except -jar) \
* -javaagent:(path-to-gf-client.jar)=(option string for our agent) \
* (main class setting: "-jar x.jar" or "a.b.Main" or "path-to-file.class")
* (arguments to be passed to the client)
* }
*
*
* The general design of this class uses several inner classes, CommandLineElement and its extensions. These classes
* have slightly different behavior depending on the specific type of command line element each represents. Each has a
* regex pattern which it uses to decide whether it recognizes a particular command line element or not. Each also
* implements (or inherits) the processValue method which actually consumes the command line element being handled --
* and sometimes the next one as well if the element takes a value (such as -classpath).
*
* @author Tim Quinn
*/
public class CLIBootstrap {
public final static String FILE_OPTIONS_INTRODUCER = "argsfile=";
private final static String COMMA_IN_ARG_PLACEHOLDER = "+-+-+-+";
private final static boolean isDebug = System.getenv("AS_DEBUG") != null;
private final static String INPUT_ARGS = System.getenv("inputArgs");
final static String ENV_VAR_PROP_PREFIX = "acc.";
/** options to the ACC that take a value */
private final static String ACC_VALUED_OPTIONS_PATTERN = "-mainclass|-name|-xml|-configxml|-user|-password|-passwordfile|-targetserver";
/** options to the ACC that take no value */
private final static String ACC_UNVALUED_OPTIONS_PATTERN = "-textauth|-noappinvoke|-usage|-help|-debug";
private final static String JVM_VALUED_OPTIONS_PATTERN = "-classpath|-cp";
private final static String INSTALL_ROOT_PROPERTY_EXPR = "-Dcom.sun.aas.installRoot=";
private final static String SECURITY_POLICY_PROPERTY_EXPR = "-Djava.security.policy=";
private final static String SECURITY_AUTH_LOGIN_CONFIG_PROPERTY_EXPR = "-Djava.security.auth.login.config=";
private final static String SYSTEM_CLASS_LOADER_PROPERTY_EXPR = "-Djava.system.class.loader=org.glassfish.appclient.client.acc.agent.ACCAgentClassLoader";
private final static String[] ENV_VARS = { "_AS_INSTALL", "APPCPATH", "VMARGS" };
private static final LocalStringManager localStrings = new LocalStringManagerImpl(CLIBootstrap.class);
private JavaInfo java;
private GlassFishInfo gfInfo;
private UserVMArgs userVMArgs;
/**
* set up during init with various subtypes of command line elements
*/
private CommandLineElement
accValuedOptions, accUnvaluedOptions, jvmPropertySettings, jvmValuedOptions, otherJVMOptions, arguments;
/** arguments passed to the ACC Java agent */
private final AgentArgs agentArgs = new AgentArgs();
/** records how the user specifies the main class: -jar xxx.jar, -client xxx.jar, or a.b.MainClass */
private final JVMMainOption jvmMainSetting = new JVMMainOption();
/** command line elements from most specific to least specific matching pattern */
private CommandLineElement[] elementsInScanOrder;
/**
* command line elements in the order they should appear on the generated command line
*/
private CommandLineElement[] elementsInOutputOrder;
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
//Convert env vars to properties. (This makes testing easier.)
envToProps();
// Because of how Windows passes arguments, the calling Windows script assigned the input arguments to an environment
// variable. Parse that variable's value into the actual arguments.
if (INPUT_ARGS != null) {
args = convertInputArgsVariable(INPUT_ARGS);
}
String outputCommandLine = new CLIBootstrap().run(args);
if (isDebug) {
System.err.println(outputCommandLine);
}
// Write the generated java command to System.out. The calling shell script will execute this command.
//
// Using print instead of println seems to work better. Using println added a \r to the end of the last command-line
// argument on Windows under cygwin.
//
System.out.print(outputCommandLine);
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
} catch (UserError ue) {
ue.displayAndExit();
}
}
/**
* Replaces commas in an argument value (which can confuse the ACC agent argument parsing because shells strip out
* double-quotes) with a special sequence.
*
* @param input string to encode
* @return encoded string
*/
public static String encodeArg(String input) {
return input.replace(",", COMMA_IN_ARG_PLACEHOLDER);
}
/**
* Replaces occurrences of comma encoding with commas.
*
* @param input possibly encoded string
* @return decoded string
*/
public static String decodeArg(String input) {
return input.replace(COMMA_IN_ARG_PLACEHOLDER, ",");
}
private static String[] convertInputArgsVariable(String inputArgs) {
/*
* The pattern matches a quoted string (double quotes around a string containing no double quote) or a non-quoted string
* (a string containing no white space or quotes).
*/
Pattern argPattern = Pattern.compile("\"([^\"]+)\"|([^\"\\s]+)");
Matcher matcher = argPattern.matcher(inputArgs);
List argList = new ArrayList();
while (matcher.find()) {
final String arg = (matcher.group(1) != null ? matcher.group(1) : matcher.group(2));
argList.add(arg);
if (isDebug) {
System.err.println("Captured argument " + arg);
}
}
return argList.toArray(new String[argList.size()]);
}
private static void envToProps() {
for (String envVar : ENV_VARS) {
String value = System.getenv(envVar);
if (value != null) {
System.setProperty(ENV_VAR_PROP_PREFIX + envVar, value);
if (isDebug) {
System.err.println(ENV_VAR_PROP_PREFIX + envVar + " set to " + value);
}
}
}
}
CLIBootstrap() throws UserError {
java = new JavaInfo();
gfInfo = new GlassFishInfo();
userVMArgs = new UserVMArgs(System.getProperty(ENV_VAR_PROP_PREFIX + "VMARGS"));
/*
* Assign the various command line element matchers. See the
* descriptions of each subtype for what each is used for.
*/
accValuedOptions = new ACCValuedOption(ACC_VALUED_OPTIONS_PATTERN);
accUnvaluedOptions = new ACCUnvaluedOption(ACC_UNVALUED_OPTIONS_PATTERN);
jvmPropertySettings = new JVMOption("-D.*", userVMArgs.evJVMPropertySettings);
jvmValuedOptions = new JVMValuedOption(JVM_VALUED_OPTIONS_PATTERN, userVMArgs.evJVMValuedOptions);
otherJVMOptions = new JVMOption("-.*", userVMArgs.evOtherJVMOptions);
arguments = new CommandLineElement(".*", Pattern.DOTALL);
initCommandLineElements();
}
/**
* Populates the command line elements collection to contain the elements from most specific matching pattern to least
* specific.
*/
private void initCommandLineElements() {
/*
* Add the elements in this order so the regex patterns will match the correct elements. In this arrangement, the
* patterns are from most specific to most general.
*/
elementsInScanOrder = new CommandLineElement[]{
accValuedOptions,
accUnvaluedOptions,
jvmValuedOptions,
jvmPropertySettings,
jvmMainSetting,
otherJVMOptions,
arguments};
/*
* Add the elements in this order so they appear in the generated java command in the correct positions.
*/
elementsInOutputOrder = new CommandLineElement[]{
jvmValuedOptions,
jvmPropertySettings,
otherJVMOptions,
accUnvaluedOptions,
accValuedOptions,
jvmMainSetting,
arguments
};
}
/**
* Places double quote marks around a string if the string is not already so enclosed.
*
* @param input
* @return the string wrapped in double quotes if not already that way; the original string otherwise
*/
private static String quote(String input) {
if (input.length() > 2 && input.charAt(0) != '"' && input.charAt(input.length() - 1) != '"') {
return '\"' + input + '\"';
}
return input;
}
/**
* Quotes the string, on non-Windows systems quoting individually any $. The shell will have replaced any env. var.
* placeholders with their values before invoking this program. Anything that looks like a placeholder now is an odd but
* legal name that should not be substituted again.
*
* @param s
* @return
*/
private static String quoteSuppressTokenSubst(final String s) {
return OS.isWindows() ? quote(s) : quote(s.replace("$", "\\$"));
}
/**
* Manages the arguments which will be passed to the ACC Java agent.
*/
private static class AgentArgs {
private final StringBuilder args = new StringBuilder("=mode=acscript");
private char sep = ',';
AgentArgs() {
String appcPath = System.getProperty(ENV_VAR_PROP_PREFIX + "APPCPATH");
if (appcPath != null && appcPath.length() > 0) {
add("appcpath=" + quote(appcPath));
}
}
/**
* Adds an ACC argument to the Java agent arguments.
*
* @param accArg
*/
final void addACCArg(final String accArg) {
add("arg=" + encodeArg(accArg));
}
/**
* Adds an item to the Java agent arguments.
*
* @param item
*/
final void add(final String item) {
args.append(sep).append(item);
}
@Override
public String toString() {
return args.toString();
}
}
/**
* A command-line element. Various subtypes have some different behavior for some of the methods.
*/
private class CommandLineElement {
private final Pattern pattern;
private final Pattern whiteSpacePattern = Pattern.compile("[\\r\\n]");
/** allows multiple values; not all command line elements support this */
final List values = new ArrayList();
CommandLineElement(String patternString) {
this(patternString, 0);
}
CommandLineElement(String patternString, int flags) {
pattern = Pattern.compile(patternString, flags);
}
boolean matches(String element) {
return matchesPattern(element);
}
final boolean matchesPattern(final String element) {
return pattern.matcher(element).matches();
}
/**
* Processes the command line element at args[slot].
*
* Subclass implementations might consume the next element as well.
*
* @param args
* @param slot
* @return next slot to be processed
* @throws UserError if the user specified an option that requires a value but provided no value (either the next
* command line element is another option or there is no next element)
*/
int processValue(String[] args, int slot) throws UserError {
// Ignore an argument that is just unquoted white space.
if (!whiteSpacePattern.matcher(args[slot]).matches()) {
values.add(args[slot++]);
} else {
slot++;
}
return slot;
}
/**
* Adds the command-line element to the Java agent arguments, if appropriate.
*
* @param element
*/
void addToAgentArgs(final String element) {
}
/**
* Returns whether there is a next argument.
*
* @param args
* @param currentSlot
* @return
*/
boolean isNextArg(String[] args, int currentSlot) {
return currentSlot < args.length - 1;
}
/**
* Returns the next argument in the array, without advancing the pointer into the array.
*
* @param args
* @param currentSlot
* @return
*/
String nextArg(String[] args, int currentSlot) {
return args[currentSlot + 1];
}
/**
* Makes sure that there is a next argument and that its value does not start with a "-" which would indicate an option,
* rather than the value for the option we are currently processing.
*
* @param args
* @param currentSlot
* @throws UserError
*/
void ensureNonOptionNextArg(final String[] args, final int currentSlot) throws UserError {
if ((currentSlot >= args.length - 1) || (args[currentSlot + 1].charAt(0) == '-')) {
throw new UserError("Command line element " + args[currentSlot] + " requires non-option value");
}
}
/**
* Adds a representation for this command-line element to the output command line.
*
* @param commandLine
* @return true if any values from this command-line element was added to the command line, false otherwise
*/
boolean format(final StringBuilder commandLine) {
return format(commandLine, true);
}
/**
* Adds a representation for this command-line element to the output command line, quoting the value if requested.
*
* @param commandLine
* @param useQuotes
* @return true if any values from this command-line element were added to the command line; false otherwise
*/
boolean format(StringBuilder commandLine, boolean useQuotes) {
boolean needSep = false;
for (String value : values) {
if (needSep) {
commandLine.append(valueSep());
}
format(commandLine, useQuotes, value);
needSep = true;
}
return !values.isEmpty();
}
/**
* Returns the separator character to be inserted in the emitted command line between values stored in the same instance
* of this command line element.
*
* @return
*/
char valueSep() {
return ' ';
}
/**
* Adds a representation for the specified value to the output command line, quoting the value if required and
*
* @param commandLine
* @param useQuotes
* @param v
* @return
*/
StringBuilder format(StringBuilder commandLine, boolean useQuotes, String v) {
if (commandLine.length() > 0) {
commandLine.append(' ');
}
commandLine.append((useQuotes ? quoteSuppressTokenSubst(v) : v));
return commandLine;
}
}
/**
* A command-line option (an element which starts with "-").
*/
private class Option extends CommandLineElement {
Option(String patternString) {
super(patternString);
}
}
/**
* A JVM command-line option. Only JVM options which appear before the main class setting are propagated to the output
* command line as JVM options. If they appear after the main class setting then they are treated as arguments to the
* client.
*
* This type of command line element can include values specified using the VMARGS environment variable.
*
*/
private class JVMOption extends Option {
JVMOption(String patternString, CommandLineElement vmargsJVMOptionElement) {
super(patternString);
if (vmargsJVMOptionElement != null) {
values.addAll(vmargsJVMOptionElement.values);
}
}
@Override
boolean matches(String element) {
/*
* Although the element might match the pattern (-.*) we do not treat this as JVM option if we have already processed
* the main class determinant.
*/
return (!jvmMainSetting.isSet()) && super.matches(element);
}
}
/**
* ACC options match anywhere on the command line unless and until we see "-jar xxx" in which case we impose the
* Java-style restriction that anything which follows the specification of the main class is an argument to be passed to
* the application.
*
* We do not impose the same restriction if the user specified -client xxx.jar in order to preserve backward
* compatibility with earlier releases, in which ACC options and client arguments could be intermixed anywhere on the
* command line.
*/
private class ACCUnvaluedOption extends Option {
ACCUnvaluedOption(final String patternString) {
super(patternString);
}
@Override
boolean matches(String element) {
return (!jvmMainSetting.isJarSetting()) && super.matches(element);
}
@Override
int processValue(String[] args, int slot) throws UserError {
int result = super.processValue(args, slot);
agentArgs.addACCArg(values.get(values.size() - 1));
return result;
}
@Override
boolean format(StringBuilder commandLine) {
/*
* We do not send ACC arguments to the Java command line. They are placed into the agent argument string instead.
*/
return false;
}
}
/**
* An option that takes a value as the next command line element.
*/
private class ValuedOption extends Option {
class OptionValue {
private String option;
private String value;
OptionValue(final String option, final String value) {
this.option = option;
this.value = value;
}
}
List optValues = new ArrayList();
ValuedOption(final String patternString) {
super(patternString);
}
@Override
int processValue(String[] args, int slot) throws UserError {
ensureNonOptionNextArg(args, slot);
optValues.add(new OptionValue(args[slot++], args[slot++]));
return slot;
}
@Override
boolean format(final StringBuilder commandLine) {
for (OptionValue ov : optValues) {
format(commandLine, false /* useQuotes */, ov.option);
format(commandLine, true /* useQuotes */, ov.value);
}
return !optValues.isEmpty();
}
}
private class JVMValuedOption extends ValuedOption {
JVMValuedOption(final String patternString, final CommandLineElement vmargsJVMValuedOption) {
super(patternString);
if (vmargsJVMValuedOption != null) {
values.addAll(vmargsJVMValuedOption.values);
}
}
@Override
boolean matches(final String element) {
return (!jvmMainSetting.isJarSetting()) && super.matches(element);
}
@Override
int processValue(String[] args, int slot) throws UserError {
ensureNonOptionNextArg(args, slot);
String key = args[slot++];
String value = args[slot++];
if(key.equals("-cp")) {
value = Stream.of(value.split(";"))
.map(cp -> quote(cp))
.collect(joining(";"));
}
optValues.add(new OptionValue(key, value));
return slot;
}
}
/**
* ACC options can appear until "-jar xxx" on the command line.
*/
private class ACCValuedOption extends ValuedOption {
ACCValuedOption(final String patternString) {
super(patternString);
}
@Override
boolean matches(final String element) {
return (!jvmMainSetting.isJarSetting()) && super.matches(element);
}
@Override
int processValue(String[] args, int slot) throws UserError {
int result = super.processValue(args, slot);
OptionValue newOptionValue = optValues.get(optValues.size() - 1);
agentArgs.addACCArg(newOptionValue.option);
agentArgs.addACCArg(quote(newOptionValue.value));
return result;
}
@Override
boolean format(final StringBuilder commandLine) {
/*
* We do not send ACC arguments to the Java command line. They are placed into the agent argument string instead.
*/
return false;
}
}
/**
* Command line element(s) with which the user specified the client to be run. Note that once "-jar xxx" is specified
* then all subsequent arguments are passed to the client as arguments. Once "-client xxx" is specified then subsequent
* arguments are treated as ACC options (if they match) or arguments to the client.
*/
private class JVMMainOption extends CommandLineElement {
private static final String JVM_MAIN_PATTERN = "-jar|-client|[^-][^\\s]*";
private String introducer = null;
private String mainClass = null;
JVMMainOption() {
super(JVM_MAIN_PATTERN);
}
boolean isJarSetting() {
return "-jar".equals(introducer);
}
boolean isClientSetting() {
return "-client".equals(introducer);
}
boolean isClassSetting() {
return mainClass != null;
}
boolean isSet() {
return !values.isEmpty();
}
@Override
int processValue(String[] args, int slot) throws UserError {
/*
* We only care about the most recent setting.
*/
values.clear();
/*
* If arg[slot] is -jar or -client we expect the next value to be the file. Make sure there is a next item and that it
* does not start with -.
*/
if (args[slot].charAt(0) == '-') {
if (nextLooksOK(args, slot)) {
introducer = args[slot++];
final int result = super.processValue(args, slot);
final String path = values.get(values.size() - 1);
final File clientSpec = new File(path);
if (clientSpec.isDirectory()) {
/*
* Record in the agent args that the user is launching a directory. Set the main class launch info to launch the ACC
* JAR.
*/
agentArgs.add("client=dir=" + quote(clientSpec.getAbsolutePath()));
introducer = "-jar";
values.set(values.size() - 1, gfInfo.agentJarPath());
} else {
agentArgs.add("client=jar=" + quote(path));
/*
* The client path is not a directory. It should be a .jar or a .ear file. If an EAR, then we want Java to launch our
* ACC jar. If a JAR, then we will launch that JAR.
*/
if (path.endsWith(".ear")) {
introducer = "-jar";
values.set(values.size() - 1, gfInfo.agentJarPath());
}
}
return result;
} else {
throw new UserError("-jar or -client requires value but missing");
}
} else {
/*
* This must be a main class specified on the command line.
*/
final int result = super.processValue(args, slot);
agentArgs.add("client=class=" + values.get(values.size() - 1));
mainClass = values.get(values.size() - 1);
return result;
}
}
@Override
boolean format(final StringBuilder commandLine) {
if(nonNull(mainClass)) {
super.format(commandLine, false /* useQuotes */, mainClass);
return true;
} else if (introducer != null) {
/*
* In the generated command we always use "-jar" to indicate the JAR to be launched, even if the user specified
* "-client" on the appclient command line.
*/
super.format(commandLine, false /* useQuotes */, "-jar");
return super.format(commandLine, true /* useQuotes */);
}
return super.format(commandLine, false /* useQuotes */);
}
private boolean nextLooksOK(final String[] args, final int slot) {
return (isNextArg(args, slot) && (args[slot + 1].charAt(0) != '-'));
}
}
/**
* Processes the user-provided command-line elements and creates the resulting output string.
*
* @param args
* @throws UserError
*/
private String run(String[] args) throws UserError {
java = new JavaInfo();
gfInfo = new GlassFishInfo();
String[] augmentedArgs = new String[args.length + 2];
augmentedArgs[0] = "-configxml";
augmentedArgs[1] = gfInfo.configxml().getAbsolutePath();
System.arraycopy(args, 0, augmentedArgs, 2, args.length);
// Process each command-line argument by the first CommandLineElement which matches the argument.
for (int i = 0; i < augmentedArgs.length;) {
boolean isMatched = false;
for (CommandLineElement commandLineElement : elementsInScanOrder) {
if (isMatched = commandLineElement.matches(augmentedArgs[i])) {
i = commandLineElement.processValue(augmentedArgs, i);
break;
}
}
if (!isMatched) {
throw new UserError("arg " + i + " = " + augmentedArgs[i] + " not recognized");
}
}
StringBuilder command = new StringBuilder(quote(java.javaExe));
addProperties(command);
if (isDebug()) {
addDebugAgentOption();
}
/*
* The user does not specify the -javaagent option we need, so we provide it here. (It is added to the appropriate
* command-line element object so, when formatted, that command-line element includes the -javaagent option.)
*/
addAgentOption();
/*
* If the user did not specify a client or usage or help then add the -usage option.
*/
if (!jvmMainSetting.isSet() && !isHelp() && !isUsage()) {
accUnvaluedOptions.processValue(new String[] { "-usage" }, 0);
}
boolean needSep = true;
for (CommandLineElement commandLineElement : elementsInOutputOrder) {
needSep = processCommandElement(command, commandLineElement, needSep);
}
return command.toString();
}
/**
* Adds JVM properties for various ACC settings.
*
* @param command
*/
private void addProperties(final StringBuilder command) {
command.append(' ')
.append(INSTALL_ROOT_PROPERTY_EXPR).append(quote(gfInfo.home().getAbsolutePath()))
.append(' ')
.append(SECURITY_POLICY_PROPERTY_EXPR).append(quote(gfInfo.securityPolicy().getAbsolutePath()))
.append(' ')
.append(SYSTEM_CLASS_LOADER_PROPERTY_EXPR)
.append(' ')
.append(SECURITY_AUTH_LOGIN_CONFIG_PROPERTY_EXPR).append(quote(gfInfo.loginConfig().getAbsolutePath()));
}
private boolean processCommandElement(final StringBuilder command, final CommandLineElement e, final boolean needSep) {
if (needSep) {
command.append(' ');
}
return e.format(command);
}
private boolean isHelp() {
return accUnvaluedOptions.values.contains("-help");
}
private boolean isUsage() {
return accUnvaluedOptions.values.contains("-usage");
}
private boolean isDebug() {
return accUnvaluedOptions.values.contains("-debug");
}
/**
* Adds the -javaagent option to the command line.
*
*/
private void addAgentOption() throws UserError {
otherJVMOptions.processValue(new String[] { "-javaagent:" + quote(gfInfo.agentJarPath()) + agentOptionsFromFile() }, 0);
}
/**
* Adds the -javaagent debug option to the command line.
*
*/
private void addDebugAgentOption() throws UserError {
otherJVMOptions.processValue(new String[] { "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"}, 0);
}
private String agentOptionsFromFile() {
try {
return '=' + FILE_OPTIONS_INTRODUCER + quote(fileContainingAgentArgs().getAbsolutePath());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private File fileContainingAgentArgs() throws IOException {
File argsFile = File.createTempFile("acc", ".dat");
try (PrintStream printStream = new PrintStream(argsFile)) {
printStream.println(agentArgs.toString());
}
return argsFile;
}
/**
* Encapsulates information about the GlassFish installation, mostly useful directories within the installation.
*
* Note that we use the property acc._AS_INSTALL to find the installation.
*/
static class GlassFishInfo {
private static final String ACC_CONFIG_PREFIX = "domains/domain1/config";
private final File home;
private final File modules;
private final File lib;
private final File libAppclient;
GlassFishInfo() {
String asInstallPath = System.getProperty(ENV_VAR_PROP_PREFIX + "_AS_INSTALL");
if (asInstallPath == null || asInstallPath.length() == 0) {
throw new IllegalArgumentException("_AS_INSTALL == null");
}
home = new File(asInstallPath);
modules = new File(home, "modules");
lib = new File(home, "lib");
libAppclient = new File(lib, "appclient");
}
File home() {
return home;
}
File modules() {
return modules;
}
File lib() {
return lib;
}
File configxml() {
// Try using glassfish-acc.xml. If that does not exist then the user might have done an in-place upgrade from an earlier
// version that used sun-acc.xml.
File configXMLFile = new File(new File(home, ACC_CONFIG_PREFIX), "glassfish-acc.xml");
if (configXMLFile.canRead()) {
return configXMLFile;
}
File sunACCXMLFile = new File(new File(home, ACC_CONFIG_PREFIX), "sun-acc.xml");
if (sunACCXMLFile.canRead()) {
return sunACCXMLFile;
}
// We found neither, but when an error is reported we want it to report the glassfish-acc.xml file is missing.
return configXMLFile;
}
String agentJarPath() {
return new File(lib, "gf-client.jar").getAbsolutePath();
}
File securityPolicy() {
return new File(libAppclient, "client.policy");
}
File loginConfig() {
return new File(libAppclient, "appclientlogin.conf");
}
}
/**
* Collects information about the current Java implementation.
*
* The user might have defined AS_JAVA or JAVA_HOME, or simply relied on the current PATH setting to choose which Java
* to use. Regardless, once this code is running SOME Java has been successfully chosen. Use the java.home property to
* find the JRE's home, which we need for the library directory (for example).
*/
static class JavaInfo {
private static final String SHELL_PROP_NAME = "org.glassfish.appclient.shell";
/*
* The appclient and appclient.bat scripts set ACCJava. Properties would be nicer instead of env vars, but the Windows
* script handling of command line args in the for statement treats the = in -Dprop=value as an argument separator and
* breaks the property assignment apart into two arguments.
*/
private static final String ACCJava_ENV_VAR_NAME = "ACCJava";
private final boolean useWindowsSyntax = File.separatorChar == '\\' && (System.getProperty(SHELL_PROP_NAME) == null);
protected String javaExe;
protected File jreHome;
JavaInfo() {
jreHome = new File(System.getProperty("java.home"));
javaExe = javaExe();
}
protected boolean isValid() {
return javaExe != null && new File(javaExe).canExecute();
}
protected File javaBinDir() {
return new File(jreHome, "bin");
}
String javaExe() {
return System.getenv(ACCJava_ENV_VAR_NAME);
}
File lib() {
return new File(jreHome, "lib");
}
String pathSeparator() {
return useWindowsSyntax ? ";" : ":";
}
}
/**
* Handles user-specified VM arguments passed by the environment variable VMARGS.
*
* This is very much like the handling of the arguments on the more general command line, except that we expect only
* valid VM arguments here.
*
* Some of the "main" CommandLineElements processed earlier in the class will use the inner command line elements here
* to augment the values they process.
*/
class UserVMArgs {
private CommandLineElement evJVMPropertySettings;
private CommandLineElement evJVMValuedOptions;
private CommandLineElement evOtherJVMOptions;
private final List evElements = new ArrayList();
UserVMArgs(final String vmargs) throws UserError {
if (isDebug) {
System.err.println("VMARGS = " + (vmargs == null ? "null" : vmargs));
}
evJVMPropertySettings = new JVMOption("-D.*", null);
evJVMValuedOptions = new JVMValuedOption(JVM_VALUED_OPTIONS_PATTERN, null);
evOtherJVMOptions = new JVMOption("-.*", null);
initEVCommandLineElements();
if (vmargs == null) {
return;
}
processEVCommandLineElements(convertInputArgsVariable(vmargs));
}
private void initEVCommandLineElements() {
evElements.add(evJVMPropertySettings);
evElements.add(evJVMValuedOptions);
evElements.add(evOtherJVMOptions);
}
private void processEVCommandLineElements(final String[] envVarJVMArgs) throws UserError {
/*
* Process each command-line argument by the first CommandLineElement which matches the argument.
*/
for (int i = 0; i < envVarJVMArgs.length;) {
boolean isMatched = false;
for (CommandLineElement commandLineElement : evElements) {
if (isMatched = commandLineElement.matches(envVarJVMArgs[i])) {
i = commandLineElement.processValue(envVarJVMArgs, i);
break;
}
}
if (!isMatched) {
throw new UserError("arg " + i + " = " + envVarJVMArgs[i] + " not recognized");
}
}
}
}
}