com.sun.enterprise.admin.launcher.GFLauncher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2008-2019 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 [2016-2023] [Payara Foundation and/or its affiliates]
package com.sun.enterprise.admin.launcher;
import com.sun.enterprise.universal.collections.CollectionUtils;
import com.sun.enterprise.universal.glassfish.ASenvPropertyReader;
import com.sun.enterprise.universal.glassfish.GFLauncherUtils;
import com.sun.enterprise.universal.glassfish.TokenResolver;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
import com.sun.enterprise.universal.io.SmartFile;
import com.sun.enterprise.universal.process.ProcessStreamDrainer;
import com.sun.enterprise.universal.xml.MiniXmlParser;
import com.sun.enterprise.universal.xml.MiniXmlParserException;
import com.sun.enterprise.util.JDK;
import com.sun.enterprise.util.OS;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.io.FileUtils;
import fish.payara.admin.launcher.PayaraDefaultJvmOptions;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.sun.enterprise.admin.launcher.GFLauncherConstants.*;
import static com.sun.enterprise.util.SystemPropertyConstants.*;
/**
* This is the main Launcher class designed for external and internal usage.
* Each of the 3 kinds of server -- domain, node-agent and instance -- need to
* subclass this class.
*
* @author bnevins
*/
public abstract class GFLauncher {
private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile(".* version \"([^\"\\-]+)(-.*)?\".*");
private final List commandLine = new ArrayList();
private final GFLauncherInfo info;
private Map asenvProps;
private JavaConfig javaConfig;
private JvmOptions jvmOptions;
private Profiler profiler;
private Map sysPropsFromXml;
private String javaExe;
private String classpath;
private String adminFileRealmKeyFile;
private boolean secureAdminEnabled;
private List debugOptions;
private long startTime;
private String logFilename;
private LaunchType mode = LaunchType.normal;
private static final LocalStringsImpl STRINGS = new LocalStringsImpl(GFLauncher.class);
private boolean setupCalledByClients = false; //handle with care
private int exitValue = -1;
private ProcessWhacker processWhacker;
private Process process;
private ProcessStreamDrainer psd;
private boolean logFilenameWasFixed = false;
private boolean needsAutoUpgrade = false;
private boolean needsManualUpgrade = false;
private int debugPort = -1;
private boolean debugSuspend = false;
///////////////////////////////////////////////////////////////////////////
////// PUBLIC api area starts here ////////////////////////
///////////////////////////////////////////////////////////////////////////
/**
*
* @return The info object that contains startup info
*/
public final GFLauncherInfo getInfo() {
return info;
}
/**
* Launches the server. Any fatal error results in a GFLauncherException No
* unchecked Throwables of any kind will be thrown.
*
* @throws com.sun.enterprise.admin.launcher.GFLauncherException
*/
@SuppressWarnings("UseSpecificCatch")
public final void launch() throws GFLauncherException {
try {
startTime = System.currentTimeMillis();
if (!setupCalledByClients) {
setup();
}
internalLaunch();
} catch (GFLauncherException gfe) {
throw gfe;
} catch (Throwable t) {
// hk2 might throw a java.lang.Error
throw new GFLauncherException(STRINGS.get("unknownError", t.getMessage()), t);
} finally {
GFLauncherLogger.removeLogFileHandler();
}
}
/**
* Launches the server - but forces the setup() to go through again.
*
* @throws com.sun.enterprise.admin.launcher.GFLauncherException
*/
public final void relaunch() throws GFLauncherException {
setupCalledByClients = false;
launch();
}
@SuppressWarnings("UseSpecificCatch")
public final void launchJVM(List cmdsIn) throws GFLauncherException {
try {
setup(); // we only use one thing -- the java executable
List commands = new LinkedList();
commands.add(javaExe);
for (String cmd : cmdsIn) {
commands.add(cmd);
}
ProcessBuilder pb = new ProcessBuilder(commands);
Process p = pb.start();
ProcessStreamDrainer.drain("launchJVM", p); // just to be safe
} catch (GFLauncherException gfe) {
throw gfe;
} catch (Throwable t) {
// hk2 might throw a java.lang.Error
throw new GFLauncherException(STRINGS.get("unknownError", t.getMessage()), t);
} finally {
GFLauncherLogger.removeLogFileHandler();
}
}
public void setup() throws GFLauncherException, MiniXmlParserException {
ASenvPropertyReader pr;
if (isFakeLaunch()) {
pr = new ASenvPropertyReader(info.getInstallDir());
} else {
pr = new ASenvPropertyReader();
}
asenvProps = pr.getProps();
info.setup();
setupLogLevels();
MiniXmlParser parser = new MiniXmlParser(getInfo().getConfigFile(), getInfo().getInstanceName());
String domainName = parser.getDomainName();
if (GFLauncherUtils.ok(domainName)) {
info.setDomainName(domainName);
}
info.setAdminAddresses(parser.getAdminAddresses());
javaConfig = new JavaConfig(parser.getJavaConfig());
// Set the config java-home value as the Java home for the environment,
// unless it is empty or it is already refering to a substitution of
// the environment variable.
String jhome = javaConfig.getJavaHome();
if (GFLauncherUtils.ok(jhome) && !jhome.trim().equals("${" + JAVA_ROOT_PROPERTY + "}")) {
asenvProps.put(JAVA_ROOT_PROPERTY, jhome);
}
setJavaExecutable();
setupProfilerAndJvmOptions(parser);
setupUpgradeSecurity();
Map realmprops = parser.getAdminRealmProperties();
if (realmprops != null) {
String classname = realmprops.get("classname");
String keyfile = realmprops.get("file");
if ("com.sun.enterprise.security.auth.realm.file.FileRealm".equals(classname) && keyfile != null) {
adminFileRealmKeyFile = keyfile;
}
}
secureAdminEnabled = parser.getSecureAdminEnabled();
renameOsgiCache();
setupMonitoring(parser);
sysPropsFromXml = parser.getSystemProperties();
asenvProps.put(INSTANCE_ROOT_PROPERTY, getInfo().getInstanceRootDir().getPath());
debugOptions = getDebug();
parseDebug();
parser.setupConfigDir(getInfo().getConfigDir(), getInfo().getInstallDir());
setLogFilename(parser);
resolveAllTokens();
fixLogFilename();
GFLauncherLogger.addLogFileHandler(logFilename);
setClasspath();
setCommandLine();
logCommandLine();
checkJDKVersion();
// if no element, we need to upgrade this domain
needsAutoUpgrade = !parser.hasNetworkConfig();
needsManualUpgrade = !parser.hasDefaultConfig();
setupCalledByClients = true;
}
public Map getSysPropsFromXml() {
return sysPropsFromXml;
}
/**
* Returns the admin realm key file for the server, if the admin realm is a
* FileRealm. Otherwise return null. This value can be used to create a
* FileRealm for the server.
* @return
*/
public String getAdminRealmKeyFile() {
return adminFileRealmKeyFile;
}
/**
* Returns true if secure admin is enabled
* @return
*/
public boolean isSecureAdminEnabled() {
return secureAdminEnabled;
}
/**
* Returns the exit value of the process. This only makes sense when we ran
* in verbose mode and waited for the process to exit in the wait() method.
* Caveat Emptor!
*
* @return the process' exit value if it completed and we waited. Otherwise
* it returns -1
*/
public final int getExitValue() {
return exitValue;
}
/**
* You don't want to call this before calling launch because it would not
* make sense.
*
* @return The Process object of the launched Server process. you will
* either get a valid Process object or an Exception will be thrown. You are
* guaranteed not to get a null.
* @throws GFLauncherException if the Process has not been created yet -
* call launch() before calling this method.
*/
public final Process getProcess() throws GFLauncherException {
if (process == null)
throw new GFLauncherException("invalid_process");
return process;
}
/**
* A ProcessStreamDrainer is always attached to every Process created here.
* It is handy for getting the stdin and stdout as a nice String.
*
* @return A valid ProcessStreamDrainer. You are guaranteed to never get a
* null.
* @throws GFLauncherException if the process has not launched yet
* @see com.sun.enterprise.universal.process.ProcessStreamDrainer
*/
public final ProcessStreamDrainer getProcessStreamDrainer() throws GFLauncherException {
if (psd == null)
throw new GFLauncherException("invalid_psd");
return psd;
}
/**
* Get the location of the server logfile
*
* @return The full path of the logfile
* @throws GFLauncherException if you call this method too early
*/
public String getLogFilename() throws GFLauncherException {
if (!logFilenameWasFixed)
throw new GFLauncherException(STRINGS.get("internalError") + " call to getLogFilename() before it has been initialized");
return logFilename;
}
/**
* Return the port number of the debug port, or -1 if debugging is not
* enabled.
*
* @return the debug port, or -1 if not debugging
*/
public final int getDebugPort() {
return debugPort;
}
/**
* Return true if suspend=y AND debugging is on. otherwise return false.
*
* @return the debug port, or -1 if not debugging
*/
public final boolean isDebugSuspend() {
return debugPort >= 0 && debugSuspend;
}
/**
* Does this domain need to be automatically upgraded before it can be
* started?
*
* @return true if the domain needs to be upgraded first
*/
public final boolean needsAutoUpgrade() {
return needsAutoUpgrade;
}
/**
* Does this domain need to be manually upgraded before it can be started?
*
* @return true if the domain needs to be upgraded first
*/
public final boolean needsManualUpgrade() {
return needsManualUpgrade;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
////// ALL private and package-private below ////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/**
* Internal launcher which in turn calls {@link #launchInstance()}
* @throws GFLauncherException
*/
abstract void internalLaunch() throws GFLauncherException;
private void parseDebug() {
// look for an option of this form:
// -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9009
// or
// -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009
// and extract the suspend and port values
for (String opt : debugOptions) {
if (isJdwpOption(opt)) {
debugPort = extractDebugPort(opt);
debugSuspend = extractDebugSuspend(opt);
}
}
}
static boolean isJdwpOption(String option) {
return option.startsWith("-Xrunjdwp:") || option.startsWith("-agentlib:jdwp");
}
static int extractDebugPort(String option) {
Pattern portRegex = Pattern.compile(".*address=(?\\d*).*");
Matcher m = portRegex.matcher(option);
if (!m.matches()) {
return -1;
}
try {
String addressGroup = m.group("port");
return Integer.parseInt(addressGroup);
} catch (NumberFormatException nfex) {
return -1;
}
}
static boolean extractDebugSuspend(String option) {
Pattern suspendRegex = Pattern.compile(".*suspend=[yY](?:,.*|$)");
Matcher m = suspendRegex.matcher(option);
return m.matches();
}
private void setLogFilename(MiniXmlParser parser) {
// Check if launching an instance to get appropriate logFilename
if (info.isInstance()) {
logFilename = parser.getInstanceLogFilename();
} else /* Assume DAS if not an instance */{
logFilename = parser.getLogFilename();
}
if (logFilename == null) {
logFilename = DEFAULT_LOGFILE;
}
}
private void fixLogFilename() throws GFLauncherException {
if (!GFLauncherUtils.ok(logFilename))
logFilename = DEFAULT_LOGFILE;
File f = new File(logFilename);
if (!f.isAbsolute()) {
// this is quite normal. Logging Service will by default return
// a relative path!
f = new File(info.getInstanceRootDir(), logFilename);
}
// Get rid of garbage like "c:/gf/./././../gf"
f = SmartFile.sanitize(f);
// if the file doesn't exist -- make sure the parent dir exists
// this is common in unit tests AND the first time the instance is
// started....
if (!f.exists()) {
File parent = f.getParentFile();
if (!parent.isDirectory()) {
boolean wasCreated = parent.mkdirs();
if (!wasCreated) {
f = null; // give up!!
}
}
}
if (f == null)
logFilename = null;
else
logFilename = f.getPath();
logFilenameWasFixed = true;
}
// unit tests will want 'fake' so that the process is not really started.
enum LaunchType {
normal, debug, trace, fake
}
void setMode(LaunchType mode) {
this.mode = mode;
}
LaunchType getMode() {
return mode;
}
boolean isFakeLaunch() {
return mode == LaunchType.fake;
}
/**
* Returns a list of all the files to be loaded as part of the main classpath.
* This includes all module jars.
* @return
* @throws GFLauncherException if Payara is running in embedded mode
*/
abstract List getMainClasspath() throws GFLauncherException;
/**
* Returns the class of Payara containg the {@code main()} function
* @return
* @throws GFLauncherException
*/
abstract String getMainClass() throws GFLauncherException;
GFLauncher(GFLauncherInfo info) {
this.info = info;
}
final Map getEnvProps() {
return asenvProps;
}
public final List getCommandLine() {
return commandLine;
}
final long getStartTime() {
return startTime;
}
/**
* Launches the instance
* @throws GFLauncherException if there was an error with the process of starting the instance
* @throws MiniXmlParserException if there was an error reading the domain.xml
*/
void launchInstance() throws GFLauncherException, MiniXmlParserException {
if (isFakeLaunch()) {
return;
}
List cmds;
// Use launchctl bsexec on MacOS versions before 10.10
// otherwise use regular startup.
// (No longer using StartupItemContext).
// See GLASSFISH-21343
if (OS.isDarwin() && useLaunchCtl(System.getProperty("os.version")) &&
(!getInfo().isVerboseOrWatchdog())) {
// On MacOS we need to start long running process with
// StartupItemContext. See IT 12942
cmds = new ArrayList();
// In MacOS 10.10 they removed StartupItemContext
// so call launchctl directly doing what StartupItemContext did
// See GLASSFISH-21113
// remove bsexec as from 10.10.3 bsexec requires sudo
cmds.add("nohup");
cmds.addAll(getCommandLine());
} else {
cmds = getCommandLine();
}
ProcessBuilder pb = new ProcessBuilder(cmds);
// change the directory if there is one specified, o/w stick with the
// default.
try {
File newDir = getInfo().getConfigDir();
pb.directory(newDir);
}
catch (Exception e) {
}
//run the process and attach Stream Drainers
try {
closeStandardStreamsMaybe();
process = pb.start();
final String name = getInfo().getDomainName();
// verbose trumps watchdog.
if (getInfo().isVerbose()) {
psd = ProcessStreamDrainer.redirect(name, process);
}
else if (getInfo().isWatchdog()) {
psd = ProcessStreamDrainer.dispose(name, process);
}
else {
psd = ProcessStreamDrainer.save(name, process);
}
writeSecurityTokens(process);
}
catch (Exception e) {
throw new GFLauncherException("jvmfailure", e, e);
}
//if verbose, hang around until the domain stops
if (getInfo().isVerboseOrWatchdog())
wait(process);
}
/**
* Checks whether to use launchctl for start up by checking if mac os version < 10.10
*
* @return True if osversion < 10.10
*/
private static boolean useLaunchCtl(String osversion) {
int major = 0;
int minor = 0;
if (osversion == null || osversion.isEmpty())
return false;
String[] split = osversion.split("[\\._\\-]+");
try {
if (split.length > 0 && split[0].length() > 0) {
major = Integer.parseInt(split[0]);
}
if (split.length > 1 && split[1].length() > 0) {
minor = Integer.parseInt(split[1]);
}
return ((major <= 9) || (major <= 10 && minor < 10));
}
catch (NumberFormatException e) {
// Assume version is 10.10 or later.
return false;
}
}
private void writeSecurityTokens(Process sp) throws GFLauncherException, IOException {
handleDeadProcess();
OutputStream os = sp.getOutputStream();
OutputStreamWriter osw = null;
BufferedWriter bw = null;
try {
osw = new OutputStreamWriter(os);
bw = new BufferedWriter(osw);
for (String token : info.securityTokens) {
bw.write(token);
bw.newLine();
bw.flush(); //flushing once is ok too
}
}
catch (IOException e) {
handleDeadProcess();
throw e; //process is not dead, but got some other exception, rethrow it
}
finally {
if (bw != null) {
bw.close();
}
if (osw != null) {
osw.close();
}
if (os != null) {
try {
os.close();
}
catch (IOException ioe) {
// nothing to do
}
}
if (bw != null) {
handleDeadProcess();
}
}
}
private void handleDeadProcess() throws GFLauncherException {
String trace = getDeadProcessTrace(process);
if (trace != null)
throw new GFLauncherException(trace);
}
private String getDeadProcessTrace(Process sp) throws GFLauncherException {
//returns null in case the process is NOT dead
try {
int ev = sp.exitValue();
ProcessStreamDrainer psd1 = getProcessStreamDrainer();
String output = psd1.getOutErrString();
return STRINGS.get("server_process_died", ev, output);
}
catch (IllegalThreadStateException e) {
//the process is still running and we are ok
return null;
}
}
void setCommandLine() throws GFLauncherException {
List cmdLine = getCommandLine();
cmdLine.clear();
addIgnoreNull(cmdLine, javaExe);
addIgnoreNull(cmdLine, "-cp");
addIgnoreNull(cmdLine, getClasspath());
addIgnoreNull(cmdLine, debugOptions);
String CLIStartTime = System.getProperty("WALL_CLOCK_START");
if (CLIStartTime != null && CLIStartTime.length() > 0) {
cmdLine.add("-DWALL_CLOCK_START=" + CLIStartTime);
}
if (jvmOptions != null)
addIgnoreNull(cmdLine, jvmOptions.toStringArray());
GFLauncherNativeHelper nativeHelper = new GFLauncherNativeHelper(info, javaConfig, jvmOptions, profiler);
addIgnoreNull(cmdLine, nativeHelper.getCommands());
addIgnoreNull(cmdLine, getMainClass());
try {
addIgnoreNull(cmdLine, getInfo().getArgsAsList());
}
catch (GFLauncherException gfle) {
throw gfle;
}
catch (Exception e) {
//harmless
}
}
/**
* Returns the Java Virtual Machine options (for testing)
* @return
*/
List getJvmOptions() {
return jvmOptions.toStringArray();
}
/**
* Adds a string to a list if it is not null
* @param list
* @param s
*/
private void addIgnoreNull(List list, String s) {
if (GFLauncherUtils.ok(s))
list.add(s);
}
private void addIgnoreNull(List list, Collection ss) {
if (ss != null && !ss.isEmpty())
list.addAll(ss);
}
private void wait(final Process p) throws GFLauncherException {
try {
setShutdownHook(p);
p.waitFor();
exitValue = p.exitValue();
}
catch (InterruptedException ex) {
throw new GFLauncherException("verboseInterruption", ex, ex);
}
}
private void setShutdownHook(final Process p) {
// ON UNIX a ^C on the console will also kill DAS
// On Windows a ^C on the console will not kill DAS
// We want UNIX behavior on Windows
// note that the hook thread will run in both cases:
// 1. the server died on its own, e.g. with a stop-domain
// 2. a ^C (or equivalent signal) was received by the console
// note that exitValue is still set to -1
// if we are restarting we may get many many processes.
// Each time this method is called we reset the Process reference inside
// the processWhacker
if (processWhacker == null) {
Runtime runtime = Runtime.getRuntime();
final String msg = STRINGS.get("serverStopped", info.getType());
processWhacker = new ProcessWhacker(p, msg);
runtime.addShutdownHook(new Thread(processWhacker));
}
else
processWhacker.setProcess(p);
}
private void resolveAllTokens() {
// resolve jvm-options against:
// 1. itself
// 2. 's from domain.xml
// 3. system properties -- essential there is, e.g. "${path.separator}" in domain.xml
// 4. asenvProps
// 5. env variables
// i.e. add in reverse order to get the precedence right
Map all = new HashMap();
Map envProps = System.getenv();
Map sysProps =
CollectionUtils.propertiesToStringMap(System.getProperties());
// TODO: Uncomment when admin password processing & aliasing is sorted out.
// Map passwordAliases = new HashMap();
// try {
// String masterPassword = "changeit";
// if (IdentityManager.getMasterPassword() != null)
// masterPassword = IdentityManager.getMasterPassword();
// PasswordAdapter pa = new PasswordAdapter(masterPassword.toCharArray());
// Enumeration e = pa.getAliases();
// if (e.hasMoreElements()) {
// String alias = (String) e.nextElement();
// passwordAliases.put(alias, pa.getPasswordForAlias(alias));
// }
// } catch (Exception e) {
// // TODO: ignore now. Defaults to not resolving password aliases
// }
// all.putAll(passwordAliases);
all.putAll(envProps);
all.putAll(asenvProps);
all.putAll(sysProps);
all.put(SystemPropertyConstants.SERVER_NAME, getInfo().getInstanceName());
all.putAll(sysPropsFromXml);
all.putAll(jvmOptions.getCombinedMap());
all.putAll(profiler.getConfig());
TokenResolver resolver = new TokenResolver(all);
resolver.resolve(jvmOptions.xProps);
resolver.resolve(jvmOptions.xxProps);
resolver.resolve(jvmOptions.plainProps);
resolver.resolve(jvmOptions.sysProps);
resolver.resolve(javaConfig.getMap());
resolver.resolve(profiler.getConfig());
resolver.resolve(debugOptions);
//resolver.resolve(sysPropsFromXml);
logFilename = resolver.resolve(logFilename);
adminFileRealmKeyFile = resolver.resolve(adminFileRealmKeyFile);
// TODO ?? Resolve sysPropsFromXml ???
}
private void setJavaExecutable() throws GFLauncherException {
// first choice is from domain.xml
if (setJavaExecutableIfValid(javaConfig.getJavaHome()))
return;
// second choice is from asenv
if (!setJavaExecutableIfValid(asenvProps.get(JAVA_ROOT_PROPERTY)))
throw new GFLauncherException("nojvm");
}
void setClasspath() throws GFLauncherException {
List mainCP = getMainClasspath(); // subclass provides this
List envCP = javaConfig.getEnvClasspath();
List sysCP = javaConfig.getSystemClasspath();
List prefixCP = javaConfig.getPrefixClasspath();
List suffixCP = javaConfig.getSuffixClasspath();
List profilerCP = profiler.getClasspath();
List extCP = Collections.singletonList(
new File(info.getInstanceRootDir(), "lib/ext/*")
);
// create a list of all the classpath pieces in the right order
List all = new ArrayList();
all.addAll(extCP);
all.addAll(prefixCP);
all.addAll(profilerCP);
all.addAll(mainCP);
all.addAll(sysCP);
all.addAll(envCP);
all.addAll(suffixCP);
setClasspath(GFLauncherUtils.fileListToPathString(all));
}
boolean setJavaExecutableIfValid(String filename) {
if (!GFLauncherUtils.ok(filename)) {
return false;
}
File f = new File(filename);
if (!f.isDirectory()) {
return false;
}
if (GFLauncherUtils.isWindows()) {
f = new File(f, "bin/java.exe");
}
else {
f = new File(f, "bin/java");
}
if (f.exists()) {
javaExe = SmartFile.sanitize(f).getPath();
return true;
}
return false;
}
private List getDebug() {
if (info.isDebug() || javaConfig.isDebugEnabled()) {
return javaConfig.getDebugOptions();
}
return Collections.emptyList();
}
private void setupProfilerAndJvmOptions(MiniXmlParser parser) throws MiniXmlParserException, GFLauncherException {
// add JVM options from Profiler *last* so they override config's
// JVM options
profiler = new Profiler(
parser.getProfilerConfig(),
parser.getProfilerJvmOptions(),
parser.getProfilerSystemProperties());
Optional jdkVersion = getConfiguredJdkVersion(javaExe);
List rawJvmOptions = parser.getJvmOptions()
.stream()
.filter(fullOption -> JDK.isCorrectJDK(jdkVersion, fullOption.vendorOrVM, fullOption.minVersion, fullOption.maxVersion))
.map(option -> option.option)
.collect(Collectors.toList());
rawJvmOptions.addAll(getSpecialSystemProperties());
if (profiler.isEnabled()) {
rawJvmOptions.addAll(profiler.getJvmOptions());
}
jvmOptions = new JvmOptions(rawJvmOptions);
if (info.isDropInterruptedCommands()) {
jvmOptions.sysProps.put(SystemPropertyConstants.DROP_INTERRUPTED_COMMANDS, Boolean.TRUE.toString());
}
// PAYARA-1681 - Add default Payara JVM options if an override isn't in place
addDefaultJvmOptions();
}
/**
* Get the Java version from the given path to a Java executable.
*
* @param javaExePath The full path to the executable java command.
* @return The Java version as a JDK.Version object, if successful.
* @throws GFLauncherException
*/
private Optional getConfiguredJdkVersion(String javaExePath) throws GFLauncherException {
try {
Runtime r = Runtime.getRuntime();
Process p = r.exec(javaExePath + " -version");
p.waitFor();
try (BufferedReader b = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {
String line;
while ((line = b.readLine()) != null) {
Matcher m = JAVA_VERSION_PATTERN.matcher(line);
if (m.matches()) {
return Optional.ofNullable(JDK.getVersion(m.group(1)));
}
}
}
return Optional.empty();
} catch (IOException | InterruptedException ex) {
throw new GFLauncherException("nojvm");
}
}
private void addDefaultJvmOptions() {
if (!jvmOptions.getCombinedMap().containsKey(PayaraDefaultJvmOptions.GRIZZLY_DEFAULT_MEMORY_MANAGER_PROPERTY)) {
jvmOptions.sysProps.put(PayaraDefaultJvmOptions.GRIZZLY_DEFAULT_MEMORY_MANAGER_PROPERTY,
PayaraDefaultJvmOptions.GRIZZLY_DEFAULT_MEMORY_MANAGER_VALUE);
// Log that we've made the change
GFLauncherLogger.fine(GFLauncherLogger.DEFAULT_JVM_OPTION,
PayaraDefaultJvmOptions.GRIZZLY_DEFAULT_MEMORY_MANAGER_PROPERTY,
PayaraDefaultJvmOptions.GRIZZLY_DEFAULT_MEMORY_MANAGER_VALUE);
}
}
private void setupUpgradeSecurity() throws GFLauncherException {
// If this is an upgrade and the security manager is on,
// copy the current server.policy file to the domain
// before the upgrade.
if (info.isUpgrade()
&& jvmOptions.sysProps.containsKey("java.security.manager")) {
GFLauncherLogger.info(GFLauncherLogger.COPY_SERVER_POLICY);
File source = new File(new File(new File(info.installDir, "lib"),
"templates"), "server.policy");
File target = new File(info.getConfigDir(), "server.policy");
try {
FileUtils.copyFile(source, target);
}
catch (IOException ioe) {
// the actual error is wrapped differently depending on
// whether the problem was with the source or target
Throwable cause = ioe.getCause() == null ? ioe : ioe.getCause();
throw new GFLauncherException(STRINGS.get(
"COPY_SERVER_POLICY_error", cause.getMessage()));
}
}
}
/**
* Because of some issues in GlassFish OSGi launcher, a server updated from
* version 3.0.x to 3.1 won't start if a OSGi cache directory populated with
* 3.0.x modules is used. So, as a work around, we rename the cache
* directory when upgrade path is used. See GLASSFISH-15772 for more
* details.
*
* @throws GFLauncherException if it fails to rename the cache directory
*/
private void renameOsgiCache() throws GFLauncherException {
if (info.isUpgrade()) {
File osgiCacheDir = new File(info.getDomainRootDir(),
"osgi-cache");
File backupOsgiCacheDir = new File(info.getDomainRootDir(),
"osgi-cache-" + System.currentTimeMillis());
if (osgiCacheDir.exists() && !backupOsgiCacheDir.exists()) {
if (!FileUtils.renameFile(osgiCacheDir, backupOsgiCacheDir)) {
throw new GFLauncherException(STRINGS.get("rename_osgi_cache_failed", osgiCacheDir, backupOsgiCacheDir));
}
else {
GFLauncherLogger.fine("rename_osgi_cache_succeeded", osgiCacheDir, backupOsgiCacheDir);
}
}
}
}
private void setupMonitoring(MiniXmlParser parser) throws GFLauncherException {
// As usual we have to be very careful.
// If it is NOT enabled -- we are out of here!!!
if (!parser.isMonitoringEnabled()) {
return;
}
// if the user has a hard-coded "-javaagent" jvm-option that uses OUR jar
// then we do NOT want to add our own.
Set plainKeys = jvmOptions.plainProps.keySet();
for (String key : plainKeys) {
if (key.startsWith("javaagent:")) {
// complications -- of course!! They may have mix&match forward and back slashes
key = key.replace('\\', '/');
if (key.contains(FLASHLIGHT_AGENT_NAME))
return; // Done!!!!
}
}
// It is not already specified AND monitoring is enabled.
try {
jvmOptions.plainProps.put(getMonitoringAgentJvmOptionString(), null);
}
catch (GFLauncherException gfe) {
// This has been defined as a non-fatal error.
// Silently ignore it -- but do NOT add it as an option
}
}
private String getMonitoringAgentJvmOptionString() throws GFLauncherException {
File libMonDir = new File(getInfo().getInstallDir(), LIBMON_NAME);
File flashlightJarFile = new File(libMonDir, FLASHLIGHT_AGENT_NAME);
if (flashlightJarFile.isFile())
return "javaagent:" + getCleanPath(flashlightJarFile);
// No agent jar...
else {
String msg = STRINGS.get("no_flashlight_agent", flashlightJarFile);
GFLauncherLogger.warning(GFLauncherLogger.NO_FLASHLIGHT_AGENT, flashlightJarFile);
throw new GFLauncherException(msg);
}
}
private static String getCleanPath(File f) {
return SmartFile.sanitize(f).getPath().replace('\\', '/');
}
private List getSpecialSystemProperties() throws GFLauncherException {
Map props = new HashMap();
props.put(INSTALL_ROOT_PROPERTY, getInfo().getInstallDir().getAbsolutePath());
props.put(INSTANCE_ROOT_PROPERTY, getInfo().getInstanceRootDir().getAbsolutePath());
return (this.propsToJvmOptions(props));
}
void logCommandLine() {
StringBuilder sb = new StringBuilder();
if (!isFakeLaunch()) {
Iterable cmdLine = getCommandLine();
for (String s : cmdLine) {
sb.append(NEWLINE);
sb.append(s);
}
GFLauncherLogger.info(GFLauncherLogger.COMMAND_LINE, sb.toString());
}
}
private void checkJDKVersion() {
if(!JDK.isRunningLTSJDK()) {
GFLauncherLogger.warning("You are running the product on an unsupported JDK version and might see unexpected results or exceptions.");
}
}
String getClasspath() {
return classpath;
}
void setClasspath(String s) {
classpath = s;
}
private List propsToJvmOptions(Map map) {
List ss = new ArrayList();
Set> entries = map.entrySet();
for (Map.Entry entry : entries) {
String name = entry.getKey();
String value = entry.getValue();
String jvm = "-D" + name;
if (value != null) {
jvm += "=" + value;
}
ss.add(jvm);
}
return ss;
}
private void setupLogLevels() {
if (info.isVerbose())
GFLauncherLogger.setConsoleLevel(Level.INFO);
else
GFLauncherLogger.setConsoleLevel(Level.WARNING);
}
@SuppressWarnings("UseSpecificCatch")
private void closeStandardStreamsMaybe() {
// see issue 12832
// Windows bug/feature -->
// Say process A (ssh) creates Process B (asadmin start-instance )
// which then fires up Process C (the instance).
// Process B exits but Process A does NOT. Process A is waiting for
// Process C to exit.
// The solution is to close down the standard streams BEFORE creating
// Process C. Then Process A becomes convinced that the process it created
// has finished.
// If there is a console that means the user is sitting at the terminal
// directly and we don't have to worry about it.
// Note that the issue is inside SSH -- not inside GF code per se. I.e.
// Process B absolutely positively does exit whether or not this code runs...
// don't run this unless we have to because our "..." messages disappear.
if (System.console() == null && OS.isWindows() && !(info.isVerboseOrWatchdog())) {
String sname;
if (info.isDomain()) {
sname = info.getDomainName();
} else {
sname = info.getInstanceName();
}
System.out.println(STRINGS.get("ssh", sname));
try {
System.in.close();
}
catch (Exception e) { // ignore
}
try {
System.err.close();
}
catch (Exception e) { // ignore
}
try {
System.out.close();
}
catch (Exception e) { // ignore
}
}
}
///////////////////////////////////////////////////////////////////////////
private static class ProcessWhacker implements Runnable {
private final String message;
private Process process;
ProcessWhacker(Process p, String msg) {
message = msg;
process = p;
}
void setProcess(Process p) {
process = p;
}
@Override
public void run() {
// we are in a shutdown hook -- most of the JVM is gone.
// logger won't work anymore...
System.out.println(message);
process.destroy();
}
}
}