
org.glassfish.appclient.client.CLIBootstrap Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.appclient.client;
import com.sun.enterprise.util.OS;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.glassfish.appclient.client.acc.UserError;
import org.glassfish.appclient.common.ClassPathUtils;
import static java.lang.System.arraycopy;
/**
* 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");
static final String ENV_VAR_PROP_PREFIX = "acc.";
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 JavaInfo java = new JavaInfo();
private GlassFishInfo gfInfo = new GlassFishInfo();
/** Records how the user specifies the main class: -jar xxx.jar, -client xxx.jar, or a.b.MainClass */
private final JVMMainOption jvmMainSetting = new JVMMainOption();
// note: must be defined after jvmMainSetting, because it uses it
private final UserVMArgs userVMArgs = new UserVMArgs(System.getProperty(ENV_VAR_PROP_PREFIX + "VMARGS"));
// Set up with various sub-types of command line elements
/** options to the ACC that take a value */
private final CommandLineElement accValuedOptions = new ACCValuedOption(
"-mainclass|-name|-xml|-configxml|-user|-password|-passwordfile|-targetserver");
/** options to the ACC that take no value */
private final CommandLineElement accUnvaluedOptions = new ACCUnvaluedOption("-textauth|-noappinvoke|-usage|-help");
private final CommandLineElement jvmValuedOptions = new JVMValuedOption("-classpath|-cp", userVMArgs.evJVMValuedOptions);
private final CommandLineElement jvmPropertySettings = new JVMOption("-D.*", userVMArgs.evJVMPropertySettings);
private final CommandLineElement otherJVMOptions = new JVMOption("-.*", userVMArgs.evOtherJVMOptions);
private final CommandLineElement arguments = new CommandLineArgument(".*", Pattern.DOTALL);
/** command line elements from most specific to least specific matching pattern */
private final CommandLineElement[] elementsInScanOrder = new CommandLineElement[] {
accValuedOptions, // collects options into "agentArgs"
accUnvaluedOptions, // collects options into "agentArgs"
jvmValuedOptions,
jvmPropertySettings,
jvmMainSetting,
otherJVMOptions,
arguments };
/**
* Command line elements in the order they should appear on the generated command line
* Add the elements in this order so they appear in the generated java command in the correct positions.
*/
private final CommandLineElement[] elementsInOutputOrder = new CommandLineElement[] {
jvmValuedOptions,
jvmPropertySettings,
otherJVMOptions,
jvmMainSetting,
arguments };
/** Arguments passed to the ACC Java agent, collected by "accValuedOptions" and "accUnvaluedOptions" */
private final AgentArgs agentArgs = new AgentArgs();
// #### Main() Methods
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
/*
* Convert env vars to properties. (This makes testing easier.)
*/
envToProps();
CLIBootstrap boot = new CLIBootstrap();
/*
* 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 = boot.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();
}
}
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);
}
}
}
}
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).
* Note:
* escapedDoubleQuoteRegex matches a double quote following odd number of
* backslash as an escaped string.
*/
final String escapedDoubleQuoteRegex = "?:(? argList = new ArrayList<>();
while (matcher.find()) {
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()]);
}
// #### Instance methods
CLIBootstrap() throws UserError {
}
JavaInfo initJava() {
return new JavaInfo();
}
/**
* 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();
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 cle : elementsInScanOrder) {
if (isMatched = cle.matches(augmentedArgs[i])) {
i = cle.processValue(augmentedArgs, i);
break;
}
}
if (!isMatched) {
throw new UserError("arg " + i + " = " + augmentedArgs[i] + " not recognized");
}
}
StringBuilder command = new StringBuilder(quote(java.javaExe));
addProperties(command);
/*
* 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 e : elementsInOutputOrder) {
needSep = processCommandElement(command, e, needSep);
}
return command.toString();
}
/**
* Adds JVM properties for various ACC settings.
*
* @param command
*/
private void addProperties(final StringBuilder command) {
command.append(' ').append("-Dorg.glassfish.gmbal.no.multipleUpperBoundsException=true");
command.append(' ').append("--add-opens=java.base/java.lang=ALL-UNNAMED");
command.append(' ').append(INSTALL_ROOT_PROPERTY_EXPR).append(quote(gfInfo.home().getAbsolutePath()));
command.append(' ').append(SECURITY_POLICY_PROPERTY_EXPR).append(quote(gfInfo.securityPolicy().getAbsolutePath()));
command.append(' ').append("-classpath").append(' ').append(gfInfo.agentJarPath()).append(File.pathSeparatorChar).append('.');
command.append(' ').append(SYSTEM_CLASS_LOADER_PROPERTY_EXPR);
command.append(' ').append("-Xshare:off");
command.append(' ').append(SECURITY_AUTH_LOGIN_CONFIG_PROPERTY_EXPR).append(quote(gfInfo.loginConfig().getAbsolutePath()));
}
/**
* Adds the -javaagent option to the command line.
*
*/
private void addAgentOption() throws UserError {
otherJVMOptions.processValue(new String[] { "-javaagent:" + quote(gfInfo.agentJarPath()) + agentOptionsFromFile() }, 0);
}
private boolean processCommandElement(StringBuilder command, CommandLineElement commandLineElement, boolean needSep) {
if (needSep) {
command.append(' ');
}
return commandLineElement.format(command);
}
// #### Static utility methods
/**
* Places double quote marks around a string if the string is not already so enclosed.
*
* @param string
* @return the string wrapped in double quotes if not already that way; the original string otherwise
*/
private static String quote(String string) {
if (string.length() > 2 && string.charAt(0) != '"' && string.charAt(string.length() - 1) != '"') {
return '\"' + string + '\"';
}
return string;
}
/**
* 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 string
* @return
*/
private static String quoteSuppressTokenSubst(String string) {
return (OS.isWindows() ? quote(string) : quote(string.replace("$", "\\$")));
}
/**
* Quotes the string, on non-Windows systems escaping metacharacters ('\', '"', '$', '`').
*
* @param string
* @return
*/
static String quoteEscapedArgument(String string) {
if (!OS.isWindows()) {
string = string.replace("\\", "\\\\").replace("\"", "\\\"").replace("$", "\\$").replace("`", "\\`");
}
return "\"" + string + "\"";
}
/**
* 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 string string to encode
* @return encoded string
*/
public static String encodeArg(String string) {
return string.replace(",", COMMA_IN_ARG_PLACEHOLDER);
}
/**
* Replaces occurrences of comma encoding with commas.
*
* @param string possibly encoded string
* @return decoded string
*/
public static String decodeArg(String string) {
return string.replace(COMMA_IN_ARG_PLACEHOLDER, ",");
}
/**
* 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 final char sep = ',';
AgentArgs() {
final String appcPath = System.getProperty(ENV_VAR_PROP_PREFIX + "APPCPATH");
if (appcPath != null && appcPath.length() > 0) {
add("appcpath=" + quote(appcPath));
}
}
/**
* Adds an item to the Java agent arguments.
*
* @param item
*/
final void add(String item) {
args.append(sep).append(item);
}
/**
* Adds an ACC argument to the Java agent arguments.
*
* @param accArg
*/
final void addACCArg(String accArg) {
add("arg=" + encodeArg(accArg));
}
@Override
public String toString() {
return args.toString();
}
}
// #### Base classes uses for the concrete elements
/**
* A command-line element. Various subtypes have some different behavior for some of the methods.
*/
private class CommandLineElement {
private final Pattern pattern;
Matcher matcher;
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);
}
final boolean matchesPattern(String element) {
matcher = pattern.matcher(element);
return matcher.matches();
}
boolean matches(String element) {
return matchesPattern(element);
}
/**
* 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.
Matcher matcher = whiteSpacePattern.matcher(args[slot]);
if (!matcher.matches()) {
values.add(args[slot++]);
} else {
slot++;
}
return slot;
}
/**
* Returns whether there is a next argument.
*
* @param args
* @param currentSlot
* @return
*/
boolean isNextArg(String[] args, int currentSlot) {
return currentSlot < args.length - 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(final 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(final StringBuilder commandLine, final boolean useQuotes, final String v) {
if (commandLine.length() > 0) {
commandLine.append(' ');
}
commandLine.append((useQuotes ? quoteSuppressTokenSubst(v) : v));
return commandLine;
}
}
class CommandLineArgument extends CommandLineElement {
CommandLineArgument(String patternString, int flags) {
super(patternString, flags);
}
@Override
StringBuilder format(final StringBuilder commandLine,
final boolean useQuotes, final String nextArg) {
if (commandLine.length() > 0) {
commandLine.append(' ');
}
commandLine.append((useQuotes ? quoteEscapedArgument(nextArg) : nextArg));
return commandLine;
}
}
/**
* A command-line option (an element which starts with "-").
*/
private class Option extends CommandLineElement {
Option(String patternString) {
super(patternString);
}
}
/**
* An option that takes a value as the next command line element.
*/
private class ValuedOption extends Option {
class OptionValue {
private final String option;
private final String value;
OptionValue(String option, 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();
}
}
// #### Concrete elements
/**
* 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 {
final int result = super.processValue(args, slot);
final 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;
}
}
/**
* 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(final String element) {
return (!jvmMainSetting.isJarSetting()) && super.matches(element);
}
@Override
int processValue(String[] args, int slot) throws UserError {
final int result = super.processValue(args, slot);
agentArgs.addACCArg(values.get(values.size() - 1));
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;
}
}
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);
}
}
/**
* 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);
}
}
/**
* 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;
JVMMainOption() {
super(JVM_MAIN_PATTERN);
}
boolean isJarSetting() {
return "-jar".equals(introducer);
}
boolean isClientSetting() {
return "-client".equals(introducer);
}
boolean isSet() {
return !values.isEmpty();
}
@Override
boolean matches(String element) {
/*
* For backward compatibility, the -client element can appear multiple times with the last appearance overriding earlier
* ones.
*/
return ((!isSet()) || ((isClientSetting() && element.equals("-client")))) && super.matches(element);
}
@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
if (args[slot].charAt(0) != '-') {
// 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));
return result;
}
if (!nextLooksOK(args, slot)) {
throw new UserError("-jar or -client requires value but missing");
}
introducer = args[slot++];
final int result = super.processValue(args, slot);
final String clientJarPath = values.get(values.size() - 1);
final File clientJarFile = new File(clientJarPath);
if (clientJarFile.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(clientJarFile.getAbsolutePath()));
introducer = "-jar";
values.set(values.size() - 1, gfInfo.agentJarPath());
} else {
agentArgs.add("client=jar=" + quote(clientJarPath));
// 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 (clientJarPath.endsWith(".ear")) {
introducer = "-jar";
values.set(values.size() - 1, gfInfo.agentJarPath());
} else if (clientJarPath.endsWith(".jar")) {
introducer = null;
values.set(values.size() - 1, "-classpath");
values.add(gfInfo.agentJarPath() + File.pathSeparatorChar + getClassPathForGfClient(clientJarPath));
String mainClass = ClassPathUtils.getMainClass(clientJarFile);
values.add(mainClass == null ? "" : mainClass);
}
}
return result;
}
private String getClassPathForGfClient(String clientJarPath) {
URL[] classpath = ClassPathUtils.getJavaClassPathForAppClient();
if (classpath.length == 0) {
return clientJarPath;
}
return clientJarPath + File.pathSeparator + Stream.of(classpath).map(ClassPathUtils::convertToString)
.collect(Collectors.joining(File.pathSeparator));
}
@Override
boolean format(final StringBuilder commandLine) {
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) != '-'));
}
}
private boolean isHelp() {
return accUnvaluedOptions.values.contains("-help");
}
private boolean isUsage() {
return accUnvaluedOptions.values.contains("-usage");
}
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");
PrintStream ps = new PrintStream(argsFile);
ps.println(agentArgs.toString());
ps.close();
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 final File home;
private final File modules;
private final File lib;
private final File libAppclient;
private static final String ACC_CONFIG_PREFIX = "domains/domain1/config";
GlassFishInfo() {
String asInstallPath = System.getProperty(ENV_VAR_PROP_PREFIX + "_AS_INSTALL");
if (asInstallPath == null || asInstallPath.length() == 0) {
throw new IllegalArgumentException("_AS_INSTALL == null");
}
this.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 extPaths() {
return new File(lib, "ext").getAbsolutePath();
}
/**
* @return gf-client.jar path
*/
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 final static 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 final static String ACCJava_ENV_VAR_NAME = "ACCJava";
private final boolean useWindowsSyntax = File.separatorChar == '\\' && (System.getProperty(SHELL_PROP_NAME) == null);
protected String javaExe;
protected File jreHome;
private JavaInfo() {
init();
}
private void init() {
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 ext() {
return new File(lib(), "ext");
}
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(String vmargs) throws UserError {
if (isDebug) {
System.err.println("VMARGS = " + (vmargs == null ? "null" : vmargs));
}
evJVMPropertySettings = new JVMOption("-D.*", null);
evJVMValuedOptions = new JVMValuedOption("-classpath|-cp", 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 cle : evElements) {
isMatched = cle.matches(envVarJVMArgs[i]);
if (isMatched) {
i = cle.processValue(envVarJVMArgs, i);
break;
}
}
if (!isMatched) {
throw new UserError("arg " + i + " = " + envVarJVMArgs[i] + " not recognized");
}
}
}
}
}