From df90c6d46cc75005906452661d767a675784a0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mat=C4=9Bj=C4=8Dek?= Date: Thu, 27 Nov 2025 22:21:46 +0100 Subject: [PATCH 1/5] GFLauncher code cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Some user messages moved to later phase so user would not miss them - Better separation of server and embedded, now I can see what is not supported by embedded's launcher - Improved user output. Signed-off-by: David Matějček --- .../admin/launcher/GFEmbeddedLauncher.java | 187 ++-- .../enterprise/admin/launcher/GFLauncher.java | 901 +++--------------- .../admin/launcher/GFLauncherConstants.java | 3 +- .../admin/launcher/GFLauncherLogger.java | 13 +- .../admin/launcher/GFLauncherMain.java | 2 +- .../admin/launcher/GlassFishMainLauncher.java | 607 +++++++++++- .../admin/launcher/LocalStrings.properties | 4 - .../admin/launcher/GFLauncherTest.java | 30 +- .../cli/ChangeAdminPasswordCommand.java | 9 +- .../servermgmt/cli/LocalServerCommand.java | 21 +- .../servermgmt/cli/RestartDomainCommand.java | 6 +- .../servermgmt/cli/ServerLifeSignChecker.java | 90 +- .../servermgmt/cli/StartDomainCommand.java | 5 +- .../servermgmt/cli/StartServerHelper.java | 33 +- .../servermgmt/domain/DomainSecurity.java | 5 +- .../admin/servermgmt/util/CommandAction.java | 71 ++ .../cluster/RestartLocalInstanceCommand.java | 6 +- .../cluster/StartLocalInstanceCommand.java | 2 +- .../universal/process/ProcessUtils.java | 5 +- .../security/common/FileRealmHelper.java | 9 +- .../security/auth/realm/file/FileRealm.java | 10 +- 21 files changed, 1008 insertions(+), 1011 deletions(-) create mode 100644 nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/util/CommandAction.java diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFEmbeddedLauncher.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFEmbeddedLauncher.java index 8e09b6d0949..0f40666eaba 100644 --- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFEmbeddedLauncher.java +++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFEmbeddedLauncher.java @@ -36,6 +36,21 @@ */ class GFEmbeddedLauncher extends GFLauncher { + private static final String GFE_RUNSERVER_JAR = "GFE_RUNSERVER_JAR"; + private static final String GFE_RUNSERVER_CLASS = "GFE_RUNSERVER_CLASS"; + private static final String GFE_JAR = "GFE_JAR"; + private static final String INSTALL_HOME = "S1AS_HOME"; + private static final String GENERAL_MESSAGE = " ********* GENERAL MESSAGE ********\n" + + "You must setup four different environmental variables to run embedded with asadmin." + + " They are\n" + + "GFE_JAR - path to the embedded jar\n" + + "S1AS_HOME - path to installation directory. This can be empty or not exist yet.\n" + + JAVA_HOME.getEnvName() + " - path to a JDK installation. JRE installation is generally not good enough\n" + + "GFE_DEBUG_PORT - optional debugging port. It will start suspended.\n" + + "\n********* SPECIFIC MESSAGE ********\n"; + + private static final String[] DERBY_FILES = { "derby.jar", "derbyclient.jar", }; + private boolean setup; private File gfeJar; private File runServerJar; @@ -43,21 +58,9 @@ class GFEmbeddedLauncher extends GFLauncher { private File javaExe; private File domainDir; private List javaDbClassPath; - private String logFilename; - private static final String GFE_RUNSERVER_JAR = "GFE_RUNSERVER_JAR"; - private static final String GFE_RUNSERVER_CLASS = "GFE_RUNSERVER_CLASS"; - private static final String GFE_JAR = "GFE_JAR"; - private static final String INSTALL_HOME = "S1AS_HOME"; - private static final String GENERAL_MESSAGE = " ********* GENERAL MESSAGE ********\n" - + "You must setup four different environmental variables to run embedded with asadmin." - + " They are\n" - + "GFE_JAR - path to the embedded jar\n" - + "S1AS_HOME - path to installation directory. This can be empty or not exist yet.\n" - + JAVA_HOME.getEnvName() + " - path to a JDK installation. JRE installation is generally not good enough\n" - + "GFE_DEBUG_PORT - optional debugging port. It will start suspended.\n" - + "\n********* SPECIFIC MESSAGE ********\n"; + private Path logFile; + private File[] classpath; - private final String[] DERBY_FILES = { "derby.jar", "derbyclient.jar", }; GFEmbeddedLauncher(GFLauncherInfo info) { super(info); @@ -65,32 +68,49 @@ class GFEmbeddedLauncher extends GFLauncher { } @Override - List getMainModulepath() throws GFLauncherException { - return List.of(); + public Long getPidBeforeRestart() { + return null; } @Override - List getMainClasspath() throws GFLauncherException { - return List.of(); + public File getAdminRealmKeyFile() { + return null; } @Override - String getMainClass() throws GFLauncherException { - String className = System.getenv(GFE_RUNSERVER_CLASS); - if (className == null) { - // FIXME: Should not be there, it is a class from now unused tests - return "org.glassfish.tests.embedded.EmbeddedMain"; - } - return className; + public boolean isSecureAdminEnabled() { + return false; } @Override - public void setup() throws GFLauncherException, MiniXmlParserException { - // remember -- this is designed exclusively for SQE usage - // don't do it mmore than once -- that would be silly! + public Integer getDebugPort() { + return null; + } + + @Override + public boolean isSuspendEnabled() { + return false; + } + + @Override + public Path getLogFile() { + return logFile; + } + + @Override + public boolean needsAutoUpgrade() { + return false; + } + + @Override + public final boolean needsManualUpgrade() { + return false; + } + @Override + public void setup() throws GFLauncherException, MiniXmlParserException { if (setup) { - return; + throw new IllegalStateException("The setup() was already executed."); } setup = true; try { @@ -99,72 +119,40 @@ public void setup() throws GFLauncherException, MiniXmlParserException { throw new GFLauncherException(GENERAL_MESSAGE + e.getMessage(), e); } - initCommandLine(); - - /* - * it is NOT an error for there to be no domain.xml (yet). so eat exceptions. Also just set the default to 4848 if we - * don't find the port... - */ - - GFLauncherInfo info = getInfo(); - - try { - File parent = info.getDomainParentDir(); - String domainName = info.getDomainName(); - String instanceName = info.getInstanceName(); - - if (instanceName == null) { - instanceName = "server"; - } + final GFLauncherInfo launchParams = getParameters(); + final String instanceName = launchParams.getInstanceName() == null ? "server" : launchParams.getInstanceName(); + final File configDir = new File(domainDir, "config"); + final File domainXmlFile = new File(configDir, "domain.xml"); - File dom = new File(parent, domainName); - File theConfigDir = new File(dom, "config"); - File domainXmlFile = new File(theConfigDir, "domain.xml"); - info.setConfigDir(theConfigDir); - - info.setDomainRootDir(new File(System.getenv(INSTALL_HOME))); - MiniXmlParser parser = new MiniXmlParser(domainXmlFile, instanceName); - List adminAddresses = parser.getAdminAddresses(); - info.setAsadminAdminAddress(new HostAndPort("localhost", 4848, false)); - info.setXmlAdminAddresses(adminAddresses); - File logFile = new File(dom, "logs"); - logFile = new File(logFile, "server.log"); - logFilename = logFile.getAbsolutePath(); - - } catch (Exception e) { - // temp todo - e.printStackTrace(); - } + launchParams.setConfigDir(configDir); + launchParams.setDomainRootDir(new File(System.getenv(INSTALL_HOME))); + final MiniXmlParser parser = new MiniXmlParser(domainXmlFile, instanceName); + launchParams.setAsadminAdminAddress(new HostAndPort("localhost", 4848, false)); + launchParams.setXmlAdminAddresses(parser.getAdminAddresses()); - GFLauncherLogger.addLogFileHandler(getLogFilename()); - } + File logsDir = new File(domainDir, "logs"); + logFile = new File(logsDir, "server.log").toPath().toAbsolutePath(); + GFLauncherLogger.addLogFileHandler(logFile); - @Override - public String getLogFilename() { - return logFilename; + setCommandLine(prepareCommandLine()); } - @Override - void setClasspath() { + private void setClasspath() { List cp = new ArrayList<>(); cp.add(gfeJar); cp.addAll(javaDbClassPath); if (runServerJar != null) { cp.add(runServerJar); } - setClasspath(cp.toArray(File[]::new)); + this.classpath = cp.toArray(File[]::new); } - @Override - void initCommandLine() throws GFLauncherException { + private CommandLine prepareCommandLine() { CommandLine cmdLine = new CommandLine(CommandFormat.ProcessBuilder); cmdLine.append(javaExe.toPath()); addThreadDump(cmdLine); - if (getModulepath().length > 0) { - cmdLine.appendModulePath(getModulepath()); - } - if (getClasspath().length > 0) { - cmdLine.appendClassPath(getClasspath()); + if (classpath.length > 0) { + cmdLine.appendClassPath(classpath); } addDebug(cmdLine); cmdLine.append(getMainClass()); @@ -175,22 +163,28 @@ void initCommandLine() throws GFLauncherException { cmdLine.append("--autodelete"); cmdLine.append("false"); cmdLine.append("--autodeploy"); - setCommandLine(cmdLine); + return cmdLine; } - private void addDebug(CommandLine cmdLine) { - String suspend; - String debugPort = System.getenv("GFE_DEBUG_PORT"); - if (ok(debugPort)) { - suspend = "y"; - } else { - debugPort = "12345"; - suspend = "n"; + private String getMainClass() { + String className = System.getenv(GFE_RUNSERVER_CLASS); + if (className == null) { + // FIXME: Should not be there, it is a class from now unused tests + return "org.glassfish.tests.embedded.EmbeddedMain"; } + return className; + } + private void addDebug(CommandLine cmdLine) { + String suspend = System.getenv("GFE_DEBUG_SUSPEND"); + String debugPort = System.getenv("GFE_DEBUG_PORT"); + if (debugPort == null) { + return; + } + String suspendOption = Boolean.valueOf(suspend).booleanValue() ? "y" : "n"; cmdLine.append("-Xdebug"); - cmdLine.append("-Xrunjdwp:transport=dt_socket,server=y,suspend=" + suspend + ",address=" + debugPort); + cmdLine.append("-Xrunjdwp:transport=dt_socket,server=y,suspend=" + suspendOption + ",address=" + debugPort); } private void addThreadDump(CommandLine cmdLine) { @@ -209,12 +203,11 @@ private void setupFromEnv() throws GFLauncherException { setupDomainDir(); setupJavaDB(); setClasspath(); - setModulepath(); } private void setupDomainDir() throws GFLauncherException { - String domainDirName = getInfo().getDomainName(); - domainDir = getInfo().getDomainParentDir(); + String domainDirName = getParameters().getDomainName(); + domainDir = getParameters().getDomainParentDir(); domainDir = new File(domainDir, domainDirName); if (!FileUtils.mkdirsMaybe(domainDir)) { @@ -225,16 +218,15 @@ private void setupDomainDir() throws GFLauncherException { } private void setupJDK() throws GFLauncherException { - String err = "You must set the environmental variable " + JAVA_HOME.getEnvName() + " to point" + final String err = "You must set the environmental variable " + JAVA_HOME.getEnvName() + " to point" + " at a valid JDK. /bin/javac[.exe] must exist."; String jdkDirName = System.getenv(JAVA_HOME.getEnvName()); if (!ok(jdkDirName)) { - throw new GFLauncherException(err); + jdkDirName = System.getProperty("java.home"); } File jdkDir = new File(jdkDirName); - if (!jdkDir.isDirectory()) { throw new GFLauncherException(err); } @@ -333,8 +325,7 @@ private void setupJavaDB() throws GFLauncherException { } } - private boolean ok(String s) { - return s != null && s.length() > 0; + private static boolean ok(String s) { + return s != null && !s.isEmpty(); } - } diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java index 09158c40495..d6199e5a813 100644 --- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java +++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java @@ -17,13 +17,8 @@ package com.sun.enterprise.admin.launcher; import com.sun.enterprise.admin.launcher.CommandLine.CommandFormat; -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.process.ProcessStreamDrainer; -import com.sun.enterprise.universal.xml.MiniXmlParser; import com.sun.enterprise.universal.xml.MiniXmlParserException; -import com.sun.enterprise.util.io.FileUtils; import java.io.BufferedWriter; import java.io.File; @@ -35,289 +30,135 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.sun.enterprise.admin.launcher.GFLauncher.LaunchType.fake; -import static com.sun.enterprise.admin.launcher.GFLauncherConstants.DEFAULT_LOGFILE; -import static com.sun.enterprise.admin.launcher.GFLauncherConstants.FLASHLIGHT_AGENT_NAME; -import static com.sun.enterprise.admin.launcher.GFLauncherConstants.LIBMON_NAME; import static com.sun.enterprise.admin.launcher.GFLauncherLogger.COMMAND_LINE; -import static com.sun.enterprise.universal.collections.CollectionUtils.propertiesToStringMap; -import static com.sun.enterprise.universal.glassfish.GFLauncherUtils.ok; import static com.sun.enterprise.util.OS.isDarwin; -import static com.sun.enterprise.util.SystemPropertyConstants.DEBUG_MODE_PROPERTY; -import static com.sun.enterprise.util.SystemPropertyConstants.DISABLE_ENV_VAR_EXPANSION_PROPERTY; -import static com.sun.enterprise.util.SystemPropertyConstants.DROP_INTERRUPTED_COMMANDS; -import static com.sun.enterprise.util.SystemPropertyConstants.PREFER_ENV_VARS_OVER_PROPERTIES; -import static java.lang.Boolean.TRUE; -import static java.lang.System.Logger.Level.INFO; -import static java.lang.System.Logger.Level.WARNING; +import static java.lang.System.Logger.Level.DEBUG; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; -import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; -import static org.glassfish.embeddable.GlassFishVariable.INSTALL_ROOT; -import static org.glassfish.embeddable.GlassFishVariable.INSTANCE_ROOT; -import static org.glassfish.embeddable.GlassFishVariable.JAVA_ROOT; /** * 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. + * Each of kinds of server, domain, node-agent and instance, need to subclass this class. * * @author bnevins */ public abstract class GFLauncher { private static final Logger LOG = System.getLogger(GFLauncher.class.getName()); + private LaunchType mode = LaunchType.normal; + /** * Parameters provided by the caller of a launcher, either programmatically * (for GF embedded) or as commandline parameters (GF DAS or Instance). * */ - private final GFLauncherInfo callerParameters; + private final GFLauncherInfo parameters; /** - * Properties from asenv.conf, such as - * AS_DEF_DOMAINS_PATH="../domains" - */ - private Map asenvProps; - - /** - * The java-config attributes in domain.xml + * The full commandline string used to start GlassFish in process + * glassFishProcess */ - private JavaConfig domainXMLjavaConfig; + private CommandLine commandLine; /** - * the debug-options attribute from java-config in - * domain.xml + * Time when GlassFish was launched */ - private List domainXMLjavaConfigDebugOptions; + private long startTime; /** - * The debug port (address) primarily extracted from - * domainXMLjavaConfigDebugOptions + * The process which is running GlassFish */ - private Integer debugPort; + private Process glassFishProcess; + private ProcessStreamDrainer processStreamDrainer; /** - * The debug suspend (suspend) primarily extracted from - * domainXMLjavaConfigDebugOptions + * Exit value of the glassFishProcess, IFF we waited on the process */ - private boolean debugSuspend; + private int exitValue = -1; - /** - * The combined jvm-options from java-config, - * profiler in domain.xml and extra ones added by this launcher - */ - private JvmOptions domainXMLjvmOptions; - /** - * Same data as domainXMLjvmOptions, but as list - */ - private final List domainXMLJvmOptionsAsList = new ArrayList<>(); + protected GFLauncher(GFLauncherInfo parameters) { + this.parameters = parameters; + } - /** - * The profiler from java-config in domain.xml - */ - private Profiler domainXMLJavaConfigProfiler; /** - * The (combined) system-property elements in domain.xml + * @return PID of the previous JVM process of the server, sent from that as a system property. Can be null. */ - private Map domainXMLSystemProperty; - - private Path javaExe; - private File[] modulepath; - private File[] classpath; - private String adminFileRealmKeyFile; - private boolean secureAdminEnabled; + public abstract Long getPidBeforeRestart(); /** - * The file name to log to using a - * java.util.logging.FileHandler.FileHandler + * Execute launch preparations. Configure the launcher, prepare the command line, some files. + * + * @throws GFLauncherException + * @throws MiniXmlParserException */ - private String logFilename; // defaults to "logs/server.log" + public abstract void setup() throws GFLauncherException, MiniXmlParserException; /** - * Tracks whether the fixLogFileName() method was called. + * @return 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. */ - private boolean logFilenameWasFixed; + public abstract File getAdminRealmKeyFile(); - // Tracks upgrading from V2 to V3. Since we're at V6 atm of wring, is this really needed? - private boolean needsAutoUpgrade; - private boolean needsManualUpgrade; - - private LaunchType mode = LaunchType.normal; /** - * The full commandline string used to start GlassFish in process - * glassFishProcess + * @return true if secure admin is enabled */ - private CommandLine commandLine; + public abstract boolean isSecureAdminEnabled(); /** - * Time when GlassFish was launched + * Get the location of the server logfile + * + * @return The full path of the logfile */ - private long startTime; + public abstract Path getLogFile(); /** - * The process which is running GlassFish + * @return null or a port number */ - private Process glassFishProcess; - private ProcessStreamDrainer processStreamDrainer; + public abstract Integer getDebugPort(); /** - * Exit value of the glassFishProcess, IFF we waited on the process + * @return true if {@link #getDebugPort()} is not null and server's JVM is set to start in suspended mode. */ - private int exitValue = -1; - - private Long pidBeforeRestart; - - GFLauncher(GFLauncherInfo info) { - this.callerParameters = info; - } - - - abstract List getMainModulepath() throws GFLauncherException; - - abstract List getMainClasspath() throws GFLauncherException; - - abstract String getMainClass() throws GFLauncherException; + public abstract boolean isSuspendEnabled(); /** - * Launches the server. + * Does this domain need to be automatically upgraded before it can be + * started? * - * @throws GFLauncherException if launch failed. + * @return true if the domain needs to be upgraded first */ - public final void launch() throws GFLauncherException { - if (debugPort != null) { - if (debugSuspend) { - LOG.log(INFO, - () -> "Debugging will be available and the server will start suspended on port " + debugPort + "."); - } else { - LOG.log(INFO, () -> "Debugging will be available on port " + debugPort + "."); - } - } - logCommandLine(); - try { - startTime = System.currentTimeMillis(); - if (isFakeLaunch()) { - return; - } - launchInstance(); - } catch (GFLauncherException e) { - throw e; - } catch (Exception e) { - throw new GFLauncherException(e); - } finally { - GFLauncherLogger.removeLogFileHandler(); - } - } - - public void setup() throws GFLauncherException, MiniXmlParserException { - asenvProps = getAsEnvConfReader().getProps(); - pidBeforeRestart = resolvePidBeforeRestart(); - - callerParameters.setup(); - setupLogLevels(); - - MiniXmlParser domainXML = new MiniXmlParser(callerParameters.getConfigFile(), callerParameters.getInstanceName()); - - String domainName = domainXML.getDomainName(); - if (ok(domainName)) { - callerParameters.setDomainName(domainName); - } - - // Can be empty! Then local commands will set what they have from user - callerParameters.setXmlAdminAddresses(domainXML.getAdminAddresses()); - domainXMLjavaConfig = new JavaConfig(domainXML.getJavaConfig()); - setupProfilerAndJvmOptions(domainXML); - setupUpgradeSecurity(); - - Map realmprops = domainXML.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 = domainXML.getSecureAdminEnabled(); - - renameOsgiCache(); - setupMonitoring(domainXML); - domainXMLSystemProperty = domainXML.getSystemProperties(); - asenvProps.put(INSTANCE_ROOT.getSystemPropertyName(), callerParameters.getInstanceRootDir().getPath()); - - // Set the config java-home value as the Java home for the environment, - // unless it is empty or it is already referring to a substitution of - // the environment variable. - String javaHome = domainXMLjavaConfig.getJavaHome(); - if (ok(javaHome) && !javaHome.trim().equals(JAVA_ROOT.toExpression())) { - asenvProps.put(JAVA_ROOT.getPropertyName(), javaHome); - } - - domainXMLjavaConfigDebugOptions = getDebugOptionsFromDomainXMLJavaConfig(); - parseJavaConfigDebugOptions(); - domainXML.setupConfigDir(callerParameters.getConfigDir(), callerParameters.getInstallDir()); - - setLogFilename(domainXML); - resolveAllTokens(); - fixLogFilename(); - GFLauncherLogger.addLogFileHandler(logFilename); - - setJavaExecutable(); - setModulepath(); - setClasspath(); - initCommandLine(); - setJvmOptions(); - - // if no element, we need to upgrade this domain - needsAutoUpgrade = !domainXML.hasNetworkConfig(); - needsManualUpgrade = !domainXML.hasDefaultConfig(); - } + public abstract boolean needsAutoUpgrade(); /** + * Does this domain need to be manually upgraded before it can be started? * - * @return The callerParameters object that contains startup caller parameters - */ - public final GFLauncherInfo getInfo() { - return callerParameters; - } - - /** - * @return 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 true if the domain needs to be upgraded first */ - public String getAdminRealmKeyFile() { - return adminFileRealmKeyFile; - } + public abstract boolean needsManualUpgrade(); /** - * @return true if secure admin is enabled + * @return The callerParameters object that contains startup caller parameters */ - public boolean isSecureAdminEnabled() { - return secureAdminEnabled; + public final GFLauncherInfo getParameters() { + return parameters; } /** * Returns the exit value of the glassFishProcess. * This only makes sense when we ran in verbose mode and waited for the glassFishProcess to exit - * in the {@link #wait(Process)} method. + * in the {@link #waiForExit(Process)} method. * * @return the glassFishProcess' exit value if it completed and we waited. Otherwise it returns -1 */ @@ -325,13 +166,6 @@ public final int getExitValue() { return exitValue; } - /** - * @return PID of the previous JVM process of the server, sent from that as a system property. - */ - public Long getPidBeforeRestart() { - return this.pidBeforeRestart; - } - /** * You don't want to call this before calling launch because it would not * make sense. @@ -363,58 +197,72 @@ public final ProcessStreamDrainer getProcessStreamDrainer() { } /** - * Get the location of the server logfile + * Launches the server. * - * @return The full path of the logfile + * @throws GFLauncherException if launch failed. */ - public String getLogFilename() { - if (logFilenameWasFixed) { - return logFilename; + public final void launch() throws GFLauncherException { + logCommandLine(); + try { + startTime = System.currentTimeMillis(); + if (isFakeLaunch()) { + return; + } + launchInstance(); + + // If verbose, hang around until the domain stops + // We end otherwise, just in case server crashes, the exit value will be in the exception message. + if (parameters.isVerboseOrWatchdog()) { + exitValue = waiForExit(glassFishProcess); + } + } catch (GFLauncherException e) { + throw e; + } catch (Exception e) { + throw new GFLauncherException(e); + } finally { + GFLauncherLogger.removeLogFileHandler(); } - throw new IllegalStateException("Call to getLogFilename() before it has been initialized!"); } - /** - * @return null or a port number - */ - public final Integer getDebugPort() { - return debugPort; + + boolean isFakeLaunch() { + return mode == fake; } - /** - * 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; + public final CommandLine getCommandLine() { + return commandLine; } - /** - * 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; + protected final void setCommandLine(CommandLine commandLine) { + this.commandLine = commandLine; + } + + void setMode(LaunchType mode) { + this.mode = mode; + } + + LaunchType getMode() { + return mode; + } + + final long getStartTime() { + return startTime; } private void launchInstance() throws GFLauncherException { - final List securityTokens = callerParameters.getSecurityTokens(); + final List securityTokens = parameters.getSecurityTokens(); final List cmds = createCommand(!securityTokens.isEmpty()); // When calling cluster nodes, this will be visible in the server.log too. - System.err.println("Executing: " + cmds.stream().collect(Collectors.joining(" "))); - System.err.println("Please look at the server log for more details..."); + LOG.log(DEBUG, () -> "Executing: " + cmds.stream().collect(Collectors.joining(" "))); ProcessBuilder processBuilder = new ProcessBuilder(cmds); - if (callerParameters.isVerboseOrWatchdog()) { + if (parameters.isVerboseOrWatchdog()) { processBuilder.redirectOutput(Redirect.INHERIT); processBuilder.redirectError(Redirect.INHERIT); } // Change the directory if there is one specified, o/w stick with the default. - processBuilder.directory(callerParameters.getConfigDir()); + processBuilder.directory(parameters.getConfigDir()); // Run the glassFishProcess and attach Stream Drainers try { @@ -425,14 +273,14 @@ private void launchInstance() throws GFLauncherException { // Startup GlassFish glassFishProcess = processBuilder.start(); - String name = callerParameters.getDomainName(); + final String name = parameters.getDomainName(); // verbose trumps watchdog. - if (callerParameters.isIgnoreOutput()) { + if (parameters.isIgnoreOutput()) { processStreamDrainer = ProcessStreamDrainer.dispose(name, glassFishProcess); - } else if (callerParameters.isVerbose()) { + } else if (parameters.isVerbose()) { processStreamDrainer = ProcessStreamDrainer.redirect(name, glassFishProcess); - } else if (callerParameters.isWatchdog()) { + } else if (parameters.isWatchdog()) { processStreamDrainer = ProcessStreamDrainer.dispose(name, glassFishProcess); } else { processStreamDrainer = ProcessStreamDrainer.save(name, glassFishProcess); @@ -444,26 +292,21 @@ private void launchInstance() throws GFLauncherException { } catch (Exception e) { throw new GFLauncherException("jvmfailure", e, e); } - - // If verbose, hang around until the domain stops - if (callerParameters.isVerboseOrWatchdog()) { - wait(glassFishProcess); - } } private List createCommand(final boolean securityTokensAvailable) throws GFLauncherException { final List cmds; // Use launchctl bsexec on MacOS versions before 10.10 // otherwise use regular startup. - if (isDarwin() && useLaunchCtl(System.getProperty("os.version")) && !callerParameters.isVerboseOrWatchdog()) { + if (isDarwin() && useLaunchCtl(System.getProperty("os.version")) && !parameters.isVerboseOrWatchdog()) { cmds = new ArrayList<>(); cmds.add("launchctl"); cmds.add("bsexec"); cmds.add("/"); cmds.addAll(commandLine.toList()); } else if (commandLine.getFormat() == CommandFormat.Script) { - cmds = prepareWindowsEnvironment(commandLine, callerParameters.getConfigDir().toPath(), securityTokensAvailable); - } else if (callerParameters.isVerboseOrWatchdog()) { + cmds = prepareWindowsEnvironment(commandLine, parameters.getConfigDir().toPath(), securityTokensAvailable); + } else if (parameters.isVerboseOrWatchdog()) { cmds = new ArrayList<>(); cmds.addAll(commandLine.toList()); } else { @@ -476,526 +319,22 @@ private List createCommand(final boolean securityTokensAvailable) throws return cmds; } - boolean isFakeLaunch() { - return mode == fake; - } - - public final CommandLine getCommandLine() { - return commandLine; - } - - protected final void setCommandLine(CommandLine commandLine) { - this.commandLine = commandLine; - } - - // unit tests will want 'fake' so that the glassFishProcess is not really started. - enum LaunchType { - normal, - debug, - trace, - /** - * Useful just for unit tests so the server will not be started. - */ - fake - } - - void setMode(LaunchType mode) { - this.mode = mode; - } - - LaunchType getMode() { - return mode; - } - final long getStartTime() { - return startTime; - } - - final Map getEnvProps() { - return asenvProps; - } - - private ASenvPropertyReader getAsEnvConfReader() { - if (isFakeLaunch()) { - return new ASenvPropertyReader(callerParameters.getInstallDir()); - } - return new ASenvPropertyReader(); - } - - private List getDebugOptionsFromDomainXMLJavaConfig() { - if (callerParameters.isDebug() || callerParameters.isSuspend() || domainXMLjavaConfig.isDebugEnabled()) { - // Suspend setting from domain.xml can be overridden by caller - if (!callerParameters.isSuspend()) { - return domainXMLjavaConfig.getDebugOptions(); - } - - return domainXMLjavaConfig.getDebugOptions().stream().filter(e -> e.startsWith("-agentlib:jdwp")) - .map(e -> e.replace("suspend=n", "suspend=y")).collect(toList()); - } - - return emptyList(); - } - - /** - * - * look for an option of this form: - * -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9009 - * and extract the suspend and port values. - * - */ - private void parseJavaConfigDebugOptions() { - for (String option : domainXMLjavaConfigDebugOptions) { - if (!option.startsWith("-agentlib:jdwp")) { - continue; - } - - String[] attributes = option.substring(10).split(","); - for (String attribute : attributes) { - if (attribute.startsWith("address=")) { - try { - debugPort = Integer.parseInt(attribute.substring(10)); - } catch (NumberFormatException ex) { - debugPort = null; - } - } - - if (attribute.startsWith("suspend=")) { - try { - debugSuspend = attribute.substring(8).toLowerCase(Locale.getDefault()).equals("y"); - } catch (Exception ex) { - debugSuspend = false; - } - } - } - } - } - - private void setLogFilename(MiniXmlParser domainXML) { - logFilename = domainXML.getLogFilename(); - - if (logFilename == null) { - logFilename = DEFAULT_LOGFILE; - } - } - - 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 (if the above contain a switch to prefer env variables, then they go first, before 1. - // i.e. add in reverse order to get the precedence right - - Map all = new HashMap<>(); - - all.putAll(asenvProps); - all.putAll(propertiesToStringMap(System.getProperties())); - all.putAll(domainXMLSystemProperty); - all.putAll(domainXMLjvmOptions.getCombinedMap()); - all.putAll(domainXMLJavaConfigProfiler.getConfig()); - - if (!isEnvVarExpansionDisabled(all)) { - if (isPreferEnvOverProperties(all)) { - all.putAll(System.getenv()); - replacePropertiesWithEnvVars(domainXMLjvmOptions.xProps); - replacePropertiesWithEnvVars(domainXMLjvmOptions.xxProps); - replacePropertiesWithEnvVars(domainXMLjvmOptions.plainProps); - replacePropertiesWithEnvVars(domainXMLjvmOptions.longProps); - replacePropertiesWithEnvVars(domainXMLjvmOptions.sysProps); - } else { - System.getenv().forEach((name, value) -> all.putIfAbsent(name, value)); - } - } - - TokenResolver resolver = new TokenResolver(all); - resolver.resolve(domainXMLjvmOptions.xProps); - resolver.resolve(domainXMLjvmOptions.xxProps); - resolver.resolve(domainXMLjvmOptions.plainProps); - resolver.resolve(domainXMLjvmOptions.longProps); - resolver.resolve(domainXMLjvmOptions.sysProps); - resolver.resolve(domainXMLjavaConfig.getMap()); - resolver.resolve(domainXMLJavaConfigProfiler.getConfig()); - resolver.resolve(domainXMLjavaConfigDebugOptions); - - logFilename = resolver.resolve(logFilename); - adminFileRealmKeyFile = resolver.resolve(adminFileRealmKeyFile); - } - - private void replacePropertiesWithEnvVars(Map properties) { - Pattern invalidEnvVarCharsPattern = Pattern.compile("[^_0-9a-zA-Z]"); - for (Map.Entry entry : properties.entrySet()) { - String envValue = System.getenv(entry.getKey()); - if (envValue != null) { - entry.setValue(envValue); - } else { - final String sanitizedKey = invalidEnvVarCharsPattern - .matcher(entry.getKey()) - .replaceAll("_"); - envValue = System.getenv(sanitizedKey); - if (envValue != null) { - entry.setValue(envValue); - } else { - envValue = System.getenv(sanitizedKey.toUpperCase()); - if (envValue != null) { - entry.setValue(envValue); - } - } - } - - } - } - - private static Boolean isPreferEnvOverProperties(Map properties) { - return Boolean.parseBoolean(properties.get(PREFER_ENV_VARS_OVER_PROPERTIES)); - } - - private static boolean isEnvVarExpansionDisabled(Map all) { - return Boolean.parseBoolean(all.get(DISABLE_ENV_VAR_EXPANSION_PROPERTY)); - } - - private void fixLogFilename() throws GFLauncherException { - if (!ok(logFilename)) { - logFilename = DEFAULT_LOGFILE; - } - - File logFile = new File(logFilename); - if (!logFile.isAbsolute()) { - // this is quite normal. Logging Service will by default return a relative path! - logFile = new File(callerParameters.getInstanceRootDir(), logFilename); - } - - // Get rid of garbage like "c:/gf/./././../gf" - logFile = logFile.toPath().toAbsolutePath().normalize().toFile(); - - // 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 (!logFile.exists()) { - File parent = logFile.getParentFile(); - if (!parent.isDirectory()) { - boolean wasCreated = parent.mkdirs(); - if (!wasCreated) { - logFile = null; // give up!! - } - } - } - - if (logFile == null) { - logFilename = null; - } else { - logFilename = logFile.getPath(); - } - - logFilenameWasFixed = true; - } - - private void setJavaExecutable() throws GFLauncherException { - // first choice is from domain.xml - if (setJavaExecutableIfValid(domainXMLjavaConfig.getJavaHome())) { - return; - } - - // second choice is from asenv - if (!setJavaExecutableIfValid(asenvProps.get(JAVA_ROOT.getPropertyName()))) { - throw new GFLauncherException("nojvm"); - } - - } - - boolean setJavaExecutableIfValid(String filename) { - if (!ok(filename)) { - return false; - } - - File javaFile = new File(filename); - - if (!javaFile.isDirectory()) { - return false; - } - - if (GFLauncherUtils.isWindows()) { - javaFile = new File(javaFile, "bin/java.exe"); - } else { - javaFile = new File(javaFile, "bin/java"); - } - - if (javaFile.exists()) { - javaExe = javaFile.toPath().toAbsolutePath(); - return true; - } - - return false; - } - - void setModulepath() throws GFLauncherException { - setModulepath(getMainModulepath().toArray(File[]::new)); - } - - void setClasspath() throws GFLauncherException { - List mainCP = getMainClasspath(); - List envCP = domainXMLjavaConfig.getEnvClasspath(); - List sysCP = domainXMLjavaConfig.getSystemClasspath(); - List prefixCP = domainXMLjavaConfig.getPrefixClasspath(); - List suffixCP = domainXMLjavaConfig.getSuffixClasspath(); - List profilerCP = domainXMLJavaConfigProfiler.getClasspath(); - - // create a list of all the classpath pieces in the right order - List all = new ArrayList<>(); - all.addAll(prefixCP); - all.addAll(profilerCP); - all.addAll(mainCP); - all.addAll(sysCP); - all.addAll(envCP); - all.addAll(suffixCP); - setClasspath(all.toArray(File[]::new)); - } - - void initCommandLine() throws GFLauncherException { - final boolean useScript = !callerParameters.isVerboseOrWatchdog() && isSurviveWinUserSession(); - final CommandLine cmdLine = new CommandLine(useScript ? CommandFormat.Script : CommandFormat.ProcessBuilder); - cmdLine.append(javaExe); - if (modulepath.length > 0) { - cmdLine.appendModulePath(getModulepath()); - } - if (classpath.length > 0) { - cmdLine.appendClassPath(getClasspath()); - } - addIgnoreNull(cmdLine, domainXMLjavaConfigDebugOptions); - - String CLIStartTime = System.getProperty("WALL_CLOCK_START"); - if (CLIStartTime != null) { - cmdLine.append("-DWALL_CLOCK_START=" + CLIStartTime); - } - - if (debugPort != null) { - cmdLine.appendSystemOption(DEBUG_MODE_PROPERTY, TRUE.toString()); - } - - if (domainXMLjvmOptions != null) { - domainXMLjvmOptions.toList().forEach(cmdLine::appendJavaOption); - } - - GFLauncherNativeHelper nativeHelper = new GFLauncherNativeHelper(callerParameters, domainXMLjavaConfig, - domainXMLjvmOptions, domainXMLJavaConfigProfiler); - cmdLine.appendNativeLibraryPath(nativeHelper.getNativePath()); - addIgnoreNull(cmdLine, getMainClass()); - - try { - addIgnoreNull(cmdLine, callerParameters.getArgsAsList()); - } catch (GFLauncherException gfle) { - throw gfle; - } catch (Exception e) { - throw new GFLauncherException(e); - } - - setCommandLine(cmdLine); - } - - void setJvmOptions() { - domainXMLJvmOptionsAsList.clear(); - if (domainXMLjvmOptions != null) { - domainXMLjvmOptions.toList().forEach(domainXMLJvmOptionsAsList::add); - } - } - - void logCommandLine() { + private void logCommandLine() { if (!isFakeLaunch()) { GFLauncherLogger.info(COMMAND_LINE, commandLine.toString("\n")); } } - public final List getJvmOptions() { - return domainXMLJvmOptionsAsList; - } - - private void addIgnoreNull(CommandLine command, String s) { - if (ok(s)) { - command.append(s); - } - } - - private void addIgnoreNull(CommandLine command, Collection ss) { - if (ss != null && !ss.isEmpty()) { - ss.forEach(command::append); - } - } - - private void wait(final Process p) throws GFLauncherException { + private int waiForExit(final Process p) throws GFLauncherException { try { p.waitFor(); - exitValue = p.exitValue(); + return p.exitValue(); } catch (InterruptedException ex) { throw new GFLauncherException("verboseInterruption", ex, ex); } } - private void setupProfilerAndJvmOptions(MiniXmlParser domainXML) throws MiniXmlParserException, GFLauncherException { - // Add JVM options from Profiler *last* so they override config's JVM options - domainXMLJavaConfigProfiler = new Profiler(domainXML.getProfilerConfig(), domainXML.getProfilerJvmOptions(), - domainXML.getProfilerSystemProperties()); - - List rawJvmOptions = domainXML.getJvmOptions(); - rawJvmOptions.addAll(getSpecialSystemProperties()); - if (domainXMLJavaConfigProfiler.isEnabled()) { - rawJvmOptions.addAll(domainXMLJavaConfigProfiler.getJvmOptions()); - } - - domainXMLjvmOptions = new JvmOptions(rawJvmOptions); - if (callerParameters.isDropInterruptedCommands()) { - domainXMLjvmOptions.sysProps.put(DROP_INTERRUPTED_COMMANDS, TRUE.toString()); - } - } - - 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 (callerParameters.isUpgrade() && domainXMLjvmOptions.sysProps.containsKey("java.security.manager")) { - - GFLauncherLogger.info(GFLauncherLogger.copy_server_policy); - Path source = callerParameters.getInstallDir().toPath().resolve(Path.of("lib", "templates", "server.policy")); - Path target = callerParameters.getConfigDir().toPath().resolve("server.policy"); - try { - Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); - } 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("copy_server_policy_error", ioe, 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 (callerParameters.isUpgrade()) { - File osgiCacheDir = new File(callerParameters.getDomainRootDir(), "osgi-cache"); - File backupOsgiCacheDir = new File(callerParameters.getDomainRootDir(), "osgi-cache-" + System.currentTimeMillis()); - if (osgiCacheDir.exists() && !backupOsgiCacheDir.exists()) { - if (FileUtils.renameFile(osgiCacheDir, backupOsgiCacheDir)) { - GFLauncherLogger.fine("rename_osgi_cache_succeeded", osgiCacheDir, backupOsgiCacheDir); - } else { - throw new GFLauncherException("rename_osgi_cache_failed", osgiCacheDir, backupOsgiCacheDir); - } - } - } - } - - private void setupMonitoring(MiniXmlParser parser) { - // As usual we have to be very careful. - - // If it is NOT enabled -- we are out of here!!! - if (parser.isMonitoringEnabled() == false) { - 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 = domainXMLjvmOptions.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.indexOf(FLASHLIGHT_AGENT_NAME) >= 0) { - return; // Done!!!! - } - } - } - - // It is not already specified AND monitoring is enabled. - try { - domainXMLjvmOptions.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(callerParameters.getInstallDir(), LIBMON_NAME); - File flashlightJarFile = new File(libMonDir, FLASHLIGHT_AGENT_NAME); - - if (flashlightJarFile.isFile()) { - return "javaagent:" + flashlightJarFile.toPath().toAbsolutePath().normalize(); - } - GFLauncherLogger.warning(GFLauncherLogger.NO_FLASHLIGHT_AGENT, flashlightJarFile); - throw new GFLauncherException("no_flashlight_agent", flashlightJarFile); - } - - - private List getSpecialSystemProperties() throws GFLauncherException { - Map props = new HashMap<>(); - props.put(INSTALL_ROOT.getSystemPropertyName(), callerParameters.getInstallDir().getAbsolutePath()); - props.put(INSTANCE_ROOT.getSystemPropertyName(), callerParameters.getInstanceRootDir().getAbsolutePath()); - - return propsToJvmOptions(props); - } - - final File[] getClasspath() { - return classpath; - } - - final void setClasspath(File... classpath) { - this.classpath = classpath; - } - - File[] getModulepath() { - return modulepath; - } - - void setModulepath(File... modulepath) { - this.modulepath = modulepath; - } - - 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 (callerParameters.isVerbose()) { - GFLauncherLogger.setConsoleLevel(java.util.logging.Level.INFO); - } else { - GFLauncherLogger.setConsoleLevel(java.util.logging.Level.WARNING); - } - } - - private static Long resolvePidBeforeRestart() { - String pid = System.getProperty("AS_RESTART_PREVIOUS_PID"); - if (pid == null) { - return null; - } - try { - return Long.valueOf(pid); - } catch (NumberFormatException e) { - LOG.log(WARNING, "Cannot parse pid {0} required for waiting for the death of the parent process.", pid); - return null; - } - } - /** * Checks whether to use launchctl for start up by checking if mac os * version < 10.10 @@ -1028,23 +367,10 @@ private static boolean useLaunchCtl(String osversion) { } } - private static boolean isSurviveWinUserSession() { - String surviveSessionValue = System.getenv("AS_SURVIVE_WIN_USER_SESSION"); - if (surviveSessionValue == null) { - return isWindows() && isOverSSHSession(); - } - return Boolean.parseBoolean(surviveSessionValue); - } - - private static boolean isWindows() { + static boolean isWindows() { return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win"); } - private static boolean isOverSSHSession() { - return System.getenv("SSH_CLIENT") != null || System.getenv("SSH_CONNECTION") != null - || System.getenv("SSH_TTY") != null; - } - private static List prepareWindowsEnvironment(final CommandLine command, final Path configDir, final boolean stdinPreloaded) throws GFLauncherException { try { @@ -1194,11 +520,22 @@ private static String getDeadProcessTrace(Process process, ProcessStreamDrainer if (process.isAlive()) { return null; } - int ev = process.exitValue(); - if (ev == 0) { + int exitValue = process.exitValue(); + if (exitValue == 0) { return null; } - return "The server exited prematurely with exit code " + ev + return "The server exited prematurely with exit code " + exitValue + ".\nBefore it died, it produced the following output:\n\n" + drainer.getOutErrString(); } + + // unit tests will want 'fake' so that the glassFishProcess is not really started. + enum LaunchType { + normal, + debug, + trace, + /** + * Useful just for unit tests so the server will not be started. + */ + fake + } } diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherConstants.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherConstants.java index 98e00f8effb..9244494d955 100644 --- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherConstants.java +++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * Copyright (c) 2024, 2025 Contributors to the Eclipse Foundation. * Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -34,6 +34,5 @@ class GFLauncherConstants { static final String NATIVE_LIB_SUFFIX = "native-library-path-suffix"; static final String LIBMON_NAME = "lib/monitor"; static final String FLASHLIGHT_AGENT_NAME = "flashlight-agent.jar"; - static final String DEFAULT_LOGFILE = "logs/server.log"; static final boolean OS_SUPPORTS_BTRACE = !OS.isAix(); } diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherLogger.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherLogger.java index 71bb3c45019..c1408a755ef 100644 --- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherLogger.java +++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation * Copyright (c) 2008, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -18,6 +18,8 @@ package com.sun.enterprise.admin.launcher; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; @@ -79,10 +81,6 @@ public class GFLauncherLogger { @LogMessageInfo(message = "Could not locate the flashlight agent here: {0}", comment = "catastrophic error", cause = "see message", action = "Find the agent file.", level = "SEVERE") public static final String NO_FLASHLIGHT_AGENT = "NCLS-GFLAUNCHER-00003"; - @LogMessageInfo(message = "Will copy glassfish/lib/templates/server.policy file to domain before upgrading.", comment = "Upgrade Information", level = "INFO") - - public static final String copy_server_policy = "NCLS-GFLAUNCHER-00004"; - @LogMessageInfo(message = "JVM invocation command line:\n{0}", comment = "Routine Information", cause = "NA", action = "NA", level = "INFO") public static final String COMMAND_LINE = "NCLS-GFLAUNCHER-00005"; @@ -122,12 +120,13 @@ static void setConsoleLevel(Level level) { * @param logFile The logfile * @throws GFLauncherException if the info object has not been setup */ - static void addLogFileHandler(String logFile) throws GFLauncherException { + static void addLogFileHandler(Path logFile) throws GFLauncherException { try { if (logFile == null || logfileHandler != null) { return; } - logfileHandler = new FileHandler(logFile, true); + Files.createDirectories(logFile.getParent()); + logfileHandler = new FileHandler(logFile.toString(), true); logfileHandler.setFormatter(new ODLLogFormatter()); logfileHandler.setLevel(INFO); LOG.addHandler(logfileHandler); diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherMain.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherMain.java index 4e9b40f1f99..6e790e963c6 100644 --- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherMain.java +++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncherMain.java @@ -43,7 +43,7 @@ public class GFLauncherMain { public static void main(String[] args) { try { GFLauncher launcher = GFLauncherFactory.getInstance(DAS); - launcher.getInfo().addArgs(args); + launcher.getParameters().addArgs(args); launcher.setup(); launcher.launch(); } catch (GFLauncherException | MiniXmlParserException ex) { diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GlassFishMainLauncher.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GlassFishMainLauncher.java index 2e8bede10fe..1bad9efc625 100644 --- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GlassFishMainLauncher.java +++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GlassFishMainLauncher.java @@ -17,11 +17,45 @@ package com.sun.enterprise.admin.launcher; +import com.sun.enterprise.admin.launcher.CommandLine.CommandFormat; +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.xml.MiniXmlParser; +import com.sun.enterprise.universal.xml.MiniXmlParserException; +import com.sun.enterprise.util.io.FileUtils; + import java.io.File; +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import static com.sun.enterprise.admin.launcher.GFLauncherConstants.FLASHLIGHT_AGENT_NAME; +import static com.sun.enterprise.admin.launcher.GFLauncherConstants.LIBMON_NAME; +import static com.sun.enterprise.universal.collections.CollectionUtils.propertiesToStringMap; +import static com.sun.enterprise.universal.glassfish.GFLauncherUtils.ok; +import static com.sun.enterprise.util.SystemPropertyConstants.DEBUG_MODE_PROPERTY; +import static com.sun.enterprise.util.SystemPropertyConstants.DISABLE_ENV_VAR_EXPANSION_PROPERTY; +import static com.sun.enterprise.util.SystemPropertyConstants.DROP_INTERRUPTED_COMMANDS; +import static com.sun.enterprise.util.SystemPropertyConstants.PREFER_ENV_VARS_OVER_PROPERTIES; +import static java.lang.Boolean.TRUE; +import static java.lang.System.Logger.Level.INFO; +import static java.lang.System.Logger.Level.WARNING; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; import static org.glassfish.embeddable.GlassFishVariable.INSTALL_ROOT; +import static org.glassfish.embeddable.GlassFishVariable.INSTANCE_ROOT; +import static org.glassfish.embeddable.GlassFishVariable.JAVA_ROOT; /** * Prepares JVM configuration to use GlassFishMain to launch the domain or instance of the server @@ -30,9 +64,68 @@ * @author bnevins */ class GlassFishMainLauncher extends GFLauncher { + private static final Logger LOG = System.getLogger(GlassFishMainLauncher.class.getName()); private static final String MAIN_CLASS = "com.sun.enterprise.glassfish.bootstrap.GlassFishMain"; + private final Long pidBeforeRestart; + + /** + * System properties based on asenv file, but extended. + */ + private Map asenvProps; + + private Path javaExe; + + /** + * The java-config attributes in domain.xml + */ + private JavaConfig domainXMLjavaConfig; + + /** + * the debug-options attribute from java-config in + * domain.xml + */ + private List domainXMLjavaConfigDebugOptions; + + private File adminFileRealmKeyFile; + + private boolean secureAdminEnabled; + + private Path logFile; + + private boolean needsAutoUpgrade; + private boolean needsManualUpgrade; + + /** + * The profiler from java-config in domain.xml + */ + private Profiler domainXMLJavaConfigProfiler; + + /** + * The combined jvm-options from java-config, + * profiler in domain.xml and extra ones added by this launcher + */ + private JvmOptions jvmOptions; + + /** + * The debug port (address) primarily extracted from + * domainXMLjavaConfigDebugOptions + */ + private Integer debugPort; + + /** + * The debug suspend (suspend) primarily extracted from + * domainXMLjavaConfigDebugOptions + */ + private boolean debugSuspend; + + private boolean setup; + + private File modulepath; + private File[] classpath; + + // sample profiler config // // @@ -44,21 +137,521 @@ class GlassFishMainLauncher extends GFLauncher { GlassFishMainLauncher(GFLauncherInfo info) { super(info); + pidBeforeRestart = resolvePidBeforeRestart(); } @Override - List getMainClasspath() throws GFLauncherException { - return List.of(); + public Long getPidBeforeRestart() { + return this.pidBeforeRestart; } @Override - List getMainModulepath() throws GFLauncherException { - Path installRoot = new File(getEnvProps().get(INSTALL_ROOT.getPropertyName())).toPath(); - return List.of(installRoot.resolve(Path.of("lib", "bootstrap")).toAbsolutePath().normalize().toFile()); + public File getAdminRealmKeyFile() { + return adminFileRealmKeyFile; } @Override - String getMainClass() throws GFLauncherException { - return MAIN_CLASS; + public boolean isSecureAdminEnabled() { + return secureAdminEnabled; + } + + @Override + public Integer getDebugPort() { + return debugPort; + } + + @Override + public boolean isSuspendEnabled() { + return getDebugPort() != null && debugSuspend; + } + + @Override + public Path getLogFile() { + return logFile; + } + + @Override + public final boolean needsAutoUpgrade() { + return needsAutoUpgrade; + } + + @Override + public final boolean needsManualUpgrade() { + return needsManualUpgrade; + } + + + @Override + public void setup() throws GFLauncherException, MiniXmlParserException { + if (setup) { + throw new IllegalStateException("The setup() was already executed."); + } + setup = true; + final GFLauncherInfo callerParameters = getParameters(); + callerParameters.setup(); + setupLogLevels(callerParameters.isVerbose()); + + asenvProps = getAsEnvConfReader(callerParameters, isFakeLaunch()).getProps(); + + final MiniXmlParser domainXML = new MiniXmlParser(callerParameters.getConfigFile(), + callerParameters.getInstanceName()); + final String domainName = domainXML.getDomainName(); + if (ok(domainName)) { + callerParameters.setDomainName(domainName); + } + + // Can be empty! Then local commands will set what they have from user + callerParameters.setXmlAdminAddresses(domainXML.getAdminAddresses()); + domainXMLjavaConfig = new JavaConfig(domainXML.getJavaConfig()); + + domainXMLJavaConfigProfiler = createProfiler(domainXML); + jvmOptions = createJvmOptions(callerParameters, domainXMLJavaConfigProfiler, domainXML.getJvmOptions()); + + secureAdminEnabled = domainXML.getSecureAdminEnabled(); + + if (domainXML.isMonitoringEnabled()) { + setupMonitoring(callerParameters.getInstallDir()); + } + final Map domainXMLSystemProperties = domainXML.getSystemProperties(); + asenvProps.put(INSTANCE_ROOT.getSystemPropertyName(), callerParameters.getInstanceRootDir().getPath()); + + // Set the config java-home value as the Java home for the environment, + // unless it is empty or it is already referring to a substitution of + // the environment variable. + String javaHome = domainXMLjavaConfig.getJavaHome(); + if (ok(javaHome) && !javaHome.trim().equals(JAVA_ROOT.toExpression())) { + asenvProps.put(JAVA_ROOT.getPropertyName(), javaHome); + } + + domainXMLjavaConfigDebugOptions = getDebugOptionsFromDomainXMLJavaConfig(callerParameters); + setupDebugging(domainXMLjavaConfigDebugOptions); + domainXML.setupConfigDir(callerParameters.getConfigDir(), callerParameters.getInstallDir()); + + + TokenResolver resolver = createTokenResolver(domainXMLSystemProperties); + resolveAllTokens(resolver); + + adminFileRealmKeyFile = resolveAdminFileRealmKeyFile(domainXML, resolver); + logFile = resolveLogFile(domainXML.getLogFilename(), callerParameters, resolver); + GFLauncherLogger.addLogFileHandler(logFile); + + javaExe = resolveJavaExecutable(); + + final Path installRoot = new File(asenvProps.get(INSTALL_ROOT.getPropertyName())).toPath(); + modulepath = installRoot.resolve(Path.of("lib", "bootstrap")).toAbsolutePath().normalize().toFile(); + classpath = createClasspath(); + setCommandLine(prepareCommandLine(callerParameters)); + + // if no element, we need to upgrade this domain + needsAutoUpgrade = !domainXML.hasNetworkConfig(); + needsManualUpgrade = !domainXML.hasDefaultConfig(); + + // Moving and removing files + setupUpgradeSecurity(callerParameters); + renameOsgiCache(callerParameters); + } + + private CommandLine prepareCommandLine(final GFLauncherInfo callerParameters) throws GFLauncherException { + final boolean useScript = !callerParameters.isVerboseOrWatchdog() && isSurviveWinUserSession(); + final CommandLine cmdLine = new CommandLine(useScript ? CommandFormat.Script : CommandFormat.ProcessBuilder); + cmdLine.append(javaExe); + cmdLine.appendModulePath(modulepath); + if (classpath.length > 0) { + cmdLine.appendClassPath(classpath); + } + addIgnoreNull(cmdLine, domainXMLjavaConfigDebugOptions); + + final String cliStartTime = System.getProperty("WALL_CLOCK_START"); + if (cliStartTime != null) { + cmdLine.append("-DWALL_CLOCK_START=" + cliStartTime); + } + + if (getDebugPort() != null) { + cmdLine.appendSystemOption(DEBUG_MODE_PROPERTY, TRUE.toString()); + } + + jvmOptions.toList().forEach(cmdLine::appendJavaOption); + + GFLauncherNativeHelper nativeHelper = new GFLauncherNativeHelper(callerParameters, domainXMLjavaConfig, + jvmOptions, domainXMLJavaConfigProfiler); + cmdLine.appendNativeLibraryPath(nativeHelper.getNativePath()); + cmdLine.append(MAIN_CLASS); + + try { + addIgnoreNull(cmdLine, callerParameters.getArgsAsList()); + } catch (GFLauncherException gfle) { + throw gfle; + } catch (Exception e) { + throw new GFLauncherException(e); + } + return cmdLine; + } + + + private ASenvPropertyReader getAsEnvConfReader(final GFLauncherInfo callerParameters, final boolean fakeLaunch) { + return fakeLaunch ? new ASenvPropertyReader(callerParameters.getInstallDir()) : new ASenvPropertyReader(); + } + + private Path resolveJavaExecutable() throws GFLauncherException { + final Path javaFromDomainXml = getJavaExecutable(domainXMLjavaConfig.getJavaHome()); + if (javaFromDomainXml != null) { + return javaFromDomainXml; + } + final Path javaFromAsenv = getJavaExecutable(asenvProps.get(JAVA_ROOT.getPropertyName())); + if (javaFromAsenv != null) { + return javaFromAsenv; + } + throw new GFLauncherException("nojvm"); + } + + private Path getJavaExecutable(String filename) { + if (filename == null) { + return null; + } + + final File javaDir = new File(filename); + if (!javaDir.isDirectory()) { + return null; + } + + final File javaFile; + if (GFLauncherUtils.isWindows()) { + javaFile = new File(javaDir, "bin/java.exe"); + } else { + javaFile = new File(javaDir, "bin/java"); + } + + if (javaFile.exists()) { + return javaFile.toPath().toAbsolutePath(); + } + return null; + } + + + /** + * Resolves 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 (if the above contain a switch to prefer env variables, then they go first, + * before 1., i.e. add in reverse order to get the precedence right + *
+ * + * @param domainXMLSystemProperties + * @return {@link TokenResolver} + */ + private TokenResolver createTokenResolver(final Map domainXMLSystemProperties) { + Map all = new HashMap<>(); + all.putAll(asenvProps); + all.putAll(propertiesToStringMap(System.getProperties())); + all.putAll(domainXMLSystemProperties); + all.putAll(jvmOptions.getCombinedMap()); + all.putAll(domainXMLJavaConfigProfiler.getConfig()); + + if (!isEnvVarExpansionDisabled(all)) { + if (isPreferEnvOverProperties(all)) { + all.putAll(System.getenv()); + replacePropertiesWithEnvVars(jvmOptions.xProps); + replacePropertiesWithEnvVars(jvmOptions.xxProps); + replacePropertiesWithEnvVars(jvmOptions.plainProps); + replacePropertiesWithEnvVars(jvmOptions.longProps); + replacePropertiesWithEnvVars(jvmOptions.sysProps); + } else { + System.getenv().forEach((name, value) -> all.putIfAbsent(name, value)); + } + } + return new TokenResolver(all); + } + + + private void resolveAllTokens(TokenResolver resolver) { + resolver.resolve(jvmOptions.xProps); + resolver.resolve(jvmOptions.xxProps); + resolver.resolve(jvmOptions.plainProps); + resolver.resolve(jvmOptions.longProps); + resolver.resolve(jvmOptions.sysProps); + resolver.resolve(domainXMLjavaConfig.getMap()); + resolver.resolve(domainXMLJavaConfigProfiler.getConfig()); + resolver.resolve(domainXMLjavaConfigDebugOptions); + } + + private void replacePropertiesWithEnvVars(Map properties) { + Pattern invalidEnvVarCharsPattern = Pattern.compile("[^_0-9a-zA-Z]"); + for (Map.Entry entry : properties.entrySet()) { + String envValue = System.getenv(entry.getKey()); + if (envValue != null) { + entry.setValue(envValue); + } else { + final String sanitizedKey = invalidEnvVarCharsPattern + .matcher(entry.getKey()) + .replaceAll("_"); + envValue = System.getenv(sanitizedKey); + if (envValue != null) { + entry.setValue(envValue); + } else { + envValue = System.getenv(sanitizedKey.toUpperCase()); + if (envValue != null) { + entry.setValue(envValue); + } + } + } + + } + } + + private Profiler createProfiler(MiniXmlParser domainXML) throws MiniXmlParserException { + // Add JVM options from Profiler *last* so they override config's JVM options + return new Profiler(domainXML.getProfilerConfig(), domainXML.getProfilerJvmOptions(), + domainXML.getProfilerSystemProperties()); + } + + + private JvmOptions createJvmOptions(GFLauncherInfo callerParameters, Profiler profiler, + final List domainXmlJvmOptions) throws GFLauncherException { + final Map props = new HashMap<>(); + props.put(INSTALL_ROOT.getSystemPropertyName(), callerParameters.getInstallDir().getAbsolutePath()); + props.put(INSTANCE_ROOT.getSystemPropertyName(), callerParameters.getInstanceRootDir().getAbsolutePath()); + List rawJvmOptions = new ArrayList<>(domainXmlJvmOptions); + rawJvmOptions.addAll(propsToJvmOptions(props)); + if (profiler.isEnabled()) { + rawJvmOptions.addAll(profiler.getJvmOptions()); + } + JvmOptions jvm = new JvmOptions(rawJvmOptions); + if (callerParameters.isDropInterruptedCommands()) { + jvm.sysProps.put(DROP_INTERRUPTED_COMMANDS, TRUE.toString()); + } + + return jvm; + } + + private void setupUpgradeSecurity(GFLauncherInfo callerParameters) 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 (callerParameters.isUpgrade() && jvmOptions.sysProps.containsKey("java.security.manager")) { + + LOG.log(INFO, "Will copy glassfish/lib/templates/server.policy file to domain before upgrading."); + Path source = callerParameters.getInstallDir().toPath().resolve(Path.of("lib", "templates", "server.policy")); + Path target = callerParameters.getConfigDir().toPath().resolve("server.policy"); + try { + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException ioe) { + throw new GFLauncherException( + "Could not copy server.policy to domain. You may need to turn off the security manager before upgrading. " + + ioe.getMessage(), + ioe); + } + } + } + + /** + * 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(GFLauncherInfo callerParameters) throws GFLauncherException { + if (callerParameters.isUpgrade()) { + File osgiCacheDir = new File(callerParameters.getDomainRootDir(), "osgi-cache"); + File backupOsgiCacheDir = new File(callerParameters.getDomainRootDir(), "osgi-cache-" + System.currentTimeMillis()); + if (osgiCacheDir.exists() && !backupOsgiCacheDir.exists()) { + if (FileUtils.renameFile(osgiCacheDir, backupOsgiCacheDir)) { + GFLauncherLogger.fine("rename_osgi_cache_succeeded", osgiCacheDir, backupOsgiCacheDir); + } else { + throw new GFLauncherException("rename_osgi_cache_failed", osgiCacheDir, backupOsgiCacheDir); + } + } + } + } + + private void setupMonitoring(File installDirr) { + // 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.indexOf(FLASHLIGHT_AGENT_NAME) >= 0) { + return; // Done!!!! + } + } + } + + // It is not already specified AND monitoring is enabled. + try { + jvmOptions.plainProps.put(getMonitoringAgentJvmOptionString(installDirr), 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 static Boolean isPreferEnvOverProperties(Map properties) { + return Boolean.parseBoolean(properties.get(PREFER_ENV_VARS_OVER_PROPERTIES)); + } + + private static boolean isEnvVarExpansionDisabled(Map all) { + return Boolean.parseBoolean(all.get(DISABLE_ENV_VAR_EXPANSION_PROPERTY)); + } + + private List getDebugOptionsFromDomainXMLJavaConfig(GFLauncherInfo callerParameters) { + if (callerParameters.isDebug() || callerParameters.isSuspend() || domainXMLjavaConfig.isDebugEnabled()) { + // Suspend setting from domain.xml can be overridden by caller + if (!callerParameters.isSuspend()) { + return domainXMLjavaConfig.getDebugOptions(); + } + return domainXMLjavaConfig.getDebugOptions().stream().filter(e -> e.startsWith("-agentlib:jdwp")) + .map(e -> e.replace("suspend=n", "suspend=y")).collect(toList()); + } + return emptyList(); + } + private List propsToJvmOptions(Map map) { + List sysProps = 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; + } + + sysProps.add(jvm); + } + + return sysProps; + } + + private void setupLogLevels(boolean verbose) { + if (verbose) { + GFLauncherLogger.setConsoleLevel(java.util.logging.Level.INFO); + } else { + GFLauncherLogger.setConsoleLevel(java.util.logging.Level.WARNING); + } + } + + + /** create a list of all the classpath pieces in the right order */ + private File[] createClasspath() { + List all = new ArrayList<>(); + all.addAll(domainXMLjavaConfig.getPrefixClasspath()); + all.addAll(domainXMLJavaConfigProfiler.getClasspath()); + all.addAll(domainXMLjavaConfig.getSystemClasspath()); + all.addAll(domainXMLjavaConfig.getEnvClasspath()); + all.addAll(domainXMLjavaConfig.getSuffixClasspath()); + return all.toArray(File[]::new); + } + + + /** + * + * look for an option of this form: + * -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9009 + * and extract the suspend and port values. + * + */ + private void setupDebugging(List domainXMLjavaConfigDebugOptions) { + for (String option : domainXMLjavaConfigDebugOptions) { + if (!option.startsWith("-agentlib:jdwp")) { + continue; + } + + String[] attributes = option.substring(10).split(","); + for (String attribute : attributes) { + if (attribute.startsWith("address=")) { + try { + debugPort = Integer.parseInt(attribute.substring(10)); + } catch (NumberFormatException ex) { + debugPort = null; + } + } + + if (attribute.startsWith("suspend=")) { + try { + debugSuspend = attribute.substring(8).toLowerCase(Locale.getDefault()).equals("y"); + } catch (Exception ex) { + debugSuspend = false; + } + } + } + } + } + + private static String getMonitoringAgentJvmOptionString(File installDir) throws GFLauncherException { + File libMonDir = new File(installDir, LIBMON_NAME); + File flashlightJarFile = new File(libMonDir, FLASHLIGHT_AGENT_NAME); + + if (flashlightJarFile.isFile()) { + return "javaagent:" + flashlightJarFile.toPath().toAbsolutePath().normalize(); + } + GFLauncherLogger.warning(GFLauncherLogger.NO_FLASHLIGHT_AGENT, flashlightJarFile); + throw new GFLauncherException("no_flashlight_agent", flashlightJarFile); + } + + + private static File resolveAdminFileRealmKeyFile(MiniXmlParser domainXML, TokenResolver resolver) { + final Map realmprops = domainXML.getAdminRealmProperties(); + if (realmprops == null) { + return null; + } + final String classname = realmprops.get("classname"); + final String keyfile = realmprops.get("file"); + // Do not include class reference, we see it in compilation, but not in server's asadmin! + if (keyfile == null || !"com.sun.enterprise.security.auth.realm.file.FileRealm".equals(classname)) { + return null; + } + return new File(resolver.resolve(keyfile)); + } + + private static Path resolveLogFile(String domainXmlLogFile, GFLauncherInfo callerParameters, TokenResolver resolver) + throws GFLauncherException { + final String stringPath = ok(domainXmlLogFile) ? domainXmlLogFile : "logs/server.log"; + final Path logFilePath = Path.of(resolver.resolve(stringPath)); + // this is quite normal. Logging Service will by default return a relative path! + if (logFilePath.isAbsolute()) { + return logFilePath.normalize(); + } + return callerParameters.getInstanceRootDir().toPath().resolve(logFilePath).normalize(); + } + + private static Long resolvePidBeforeRestart() { + String pid = System.getProperty("AS_RESTART_PREVIOUS_PID"); + if (pid == null) { + return null; + } + try { + return Long.valueOf(pid); + } catch (NumberFormatException e) { + LOG.log(WARNING, "Cannot parse pid {0} required for waiting for the death of the parent process.", pid); + return null; + } + } + + + private static boolean isSurviveWinUserSession() { + String surviveSessionValue = System.getenv("AS_SURVIVE_WIN_USER_SESSION"); + if (surviveSessionValue == null) { + return isWindows() && isOverSSHSession(); + } + return Boolean.parseBoolean(surviveSessionValue); + } + + private static boolean isOverSSHSession() { + return System.getenv("SSH_CLIENT") != null || System.getenv("SSH_CONNECTION") != null + || System.getenv("SSH_TTY") != null; + } + + private static void addIgnoreNull(CommandLine command, Collection values) { + if (values != null && !values.isEmpty()) { + values.forEach(command::append); + } } } diff --git a/nucleus/admin/launcher/src/main/resources/com/sun/enterprise/admin/launcher/LocalStrings.properties b/nucleus/admin/launcher/src/main/resources/com/sun/enterprise/admin/launcher/LocalStrings.properties index 866026bb2e8..d4470899dd8 100644 --- a/nucleus/admin/launcher/src/main/resources/com/sun/enterprise/admin/launcher/LocalStrings.properties +++ b/nucleus/admin/launcher/src/main/resources/com/sun/enterprise/admin/launcher/LocalStrings.properties @@ -45,10 +45,6 @@ respawninfo.illegalToken=It is illegal to use this special token [{1}] in any co It was found in this argument: {0} Wait a moment longer. Always call launch before calling this method. # -# issue 11665 -copy_server_policy_error=Could not copy server.policy to domain. You may need to turn off the \ - security manager before upgrading.\nCause: {0} - #issue 15772 rename_osgi_cache_failed=Failed to rename OSGi persistence store from {0} to {1} rename_osgi_cache_succeeded=Renamed OSGi persistence store from {0} to {1} diff --git a/nucleus/admin/launcher/src/test/java/com/sun/enterprise/admin/launcher/GFLauncherTest.java b/nucleus/admin/launcher/src/test/java/com/sun/enterprise/admin/launcher/GFLauncherTest.java index 80d4ac9ee91..7ba970bbd97 100644 --- a/nucleus/admin/launcher/src/test/java/com/sun/enterprise/admin/launcher/GFLauncherTest.java +++ b/nucleus/admin/launcher/src/test/java/com/sun/enterprise/admin/launcher/GFLauncherTest.java @@ -20,12 +20,14 @@ import com.sun.enterprise.universal.xml.MiniXmlParserException; import java.io.File; +import java.nio.file.Path; import org.glassfish.api.admin.RuntimeType; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; @@ -43,7 +45,7 @@ public class GFLauncherTest { private static File installDir; private static File domainsDir; private GFLauncher launcher; - private GFLauncherInfo info; + private GFLauncherInfo launchParams; @BeforeAll @@ -65,8 +67,8 @@ public static void setUpClass() throws Exception { @BeforeEach public void initLauncher() throws Exception { launcher = GFLauncherFactory.getInstance(RuntimeType.DAS); - info = launcher.getInfo(); - info.setInstallDir(installDir); + launchParams = launcher.getParameters(); + launchParams.setInstallDir(installDir); launcher.setMode(GFLauncher.LaunchType.fake); } @@ -88,7 +90,7 @@ public void defaultDomainButMultipleDomainsExist() throws Exception { */ @Test public void domain1WithDiagOptions() throws Exception { - info.setDomainName("domain1"); + launchParams.setDomainName("domain1"); launcher.setup(); launcher.launch(); CommandLine cmdline = launcher.getCommandLine(); @@ -102,7 +104,7 @@ public void domain1WithDiagOptions() throws Exception { */ @Test public void domain2WithoutDiagOptions() throws Exception { - info.setDomainName("domain2"); + launchParams.setDomainName("domain2"); launcher.setup(); launcher.launch(); CommandLine cmdline = launcher.getCommandLine(); @@ -117,7 +119,7 @@ public void domain2WithoutDiagOptions() throws Exception { */ @Test public void missingDomain() throws Exception { - info.setDomainName("NoSuchDomain"); + launchParams.setDomainName("NoSuchDomain"); GFLauncherException e = assertThrows(GFLauncherException.class, launcher::setup); assertEquals("The domain root dir is not pointing to a directory. This is what I was looking for: [" + new File(domainsDir, "NoSuchDomain").getAbsolutePath() + "]", e.getMessage()); @@ -130,7 +132,7 @@ public void missingDomain() throws Exception { */ @Test public void brokenDomainXml() throws Exception { - info.setDomainName("baddomain"); + launchParams.setDomainName("baddomain"); MiniXmlParserException e = assertThrows(MiniXmlParserException.class, launcher::setup); assertEquals("Xml Parser Error: javax.xml.stream.XMLStreamException:" + " ParseError at [row,col]:[57,7]\n" @@ -143,10 +145,10 @@ public void brokenDomainXml() throws Exception { */ @Test public void domain1FromSGES2() throws Exception { - info.setDomainName("domain1"); + launchParams.setDomainName("domain1"); launcher.setup(); launcher.launch(); - assertTrue(launcher.getLogFilename().endsWith("server.log")); + assertThat(launcher.getLogFile().getFileName(), equalTo(Path.of("server.log"))); } /** @@ -154,18 +156,18 @@ public void domain1FromSGES2() throws Exception { */ @Test public void noLogService() throws Exception { - info.setDomainName("domainNoLog"); + launchParams.setDomainName("domainNoLog"); launcher.launch(); launcher.setup(); - assertTrue(launcher.getLogFilename().endsWith("server.log")); + assertThat(launcher.getLogFile().getFileName(), equalTo(Path.of("server.log"))); } @Test public void dropInterruptedCommands() throws Exception { - info.setDomainName("domainNoLog"); - info.setDropInterruptedCommands(true); + launchParams.setDomainName("domainNoLog"); + launchParams.setDropInterruptedCommands(true); launcher.setup(); launcher.launch(); - assertTrue(launcher.getJvmOptions().contains("-Dorg.glassfish.job-manager.drop-interrupted-commands=true")); + assertThat(launcher.getCommandLine(), hasItems("-Dorg.glassfish.job-manager.drop-interrupted-commands=true")); } } diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ChangeAdminPasswordCommand.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ChangeAdminPasswordCommand.java index ae368448d96..e6bd7302339 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ChangeAdminPasswordCommand.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ChangeAdminPasswordCommand.java @@ -29,6 +29,7 @@ import com.sun.enterprise.util.net.NetUtils; import java.io.Console; +import java.io.File; import java.io.IOException; import java.net.ConnectException; @@ -174,9 +175,9 @@ private int changeAdminPasswordLocally(String domainDir, String domainName) thro try { GFLauncher launcher = GFLauncherFactory.getInstance(RuntimeType.DAS); - GFLauncherInfo info = launcher.getInfo(); - info.setDomainName(domainName); - info.setDomainParentDir(domainDir); + GFLauncherInfo launchParams = launcher.getParameters(); + launchParams.setDomainName(domainName); + launchParams.setDomainParentDir(domainDir); launcher.setup(); //If secure admin is enabled and if new password is null @@ -187,7 +188,7 @@ private int changeAdminPasswordLocally(String domainDir, String domainName) thro } } - String adminKeyFile = launcher.getAdminRealmKeyFile(); + File adminKeyFile = launcher.getAdminRealmKeyFile(); if (adminKeyFile == null) { //Cannot change password locally for non file realms diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java index 8bf9b63a26d..8ce9e88797c 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java @@ -37,6 +37,7 @@ import java.lang.System.Logger.Level; import java.nio.file.Files; import java.time.Duration; +import java.time.Instant; import java.util.List; import java.util.function.Supplier; @@ -48,7 +49,7 @@ import static com.sun.enterprise.admin.cli.CLIConstants.DEFAULT_ADMIN_PORT; import static com.sun.enterprise.admin.cli.CLIConstants.DEFAULT_HOSTNAME; import static com.sun.enterprise.admin.cli.ProgramOptions.PasswordLocation.LOCAL_PASSWORD; -import static com.sun.enterprise.admin.servermgmt.cli.ServerLifeSignChecker.step; +import static com.sun.enterprise.admin.servermgmt.util.CommandAction.step; import static com.sun.enterprise.universal.process.ProcessUtils.loadPid; import static com.sun.enterprise.universal.process.ProcessUtils.waitForNewPid; import static com.sun.enterprise.universal.process.ProcessUtils.waitWhileIsAlive; @@ -59,7 +60,6 @@ import static com.sun.enterprise.util.SystemPropertyConstants.MASTER_PASSWORD_PASSWORD; import static com.sun.enterprise.util.SystemPropertyConstants.TRUSTSTORE_FILENAME_DEFAULT; import static java.lang.System.Logger.Level.DEBUG; -import static java.lang.System.Logger.Level.INFO; /** * A class that's supposed to capture all the behavior common to operation on a "local" server. @@ -352,31 +352,32 @@ protected final Long getServerPid() { * @param pid * @param adminAddress * @param timeout can be null + * @return remaining time * @throws CommandException if we time out. */ - protected final void waitForStop(final Long pid, final HostAndPort adminAddress, final Duration timeout) + protected final Duration waitForStop(final Long pid, final HostAndPort adminAddress, final Duration timeout) throws CommandException { LOG.log(DEBUG, "waitForStop(pid={0}, oldAdminAddress={1}, timeout={2})", pid, adminAddress, timeout); - + final Instant start = Instant.now(); final boolean printDots = !programOpts.isTerse(); final Duration portTimeout; if (pid == null) { portTimeout = timeout; } else { - portTimeout = step("Waiting for the death of the process with pid " + pid, timeout, + portTimeout = step("Waiting for process with pid " + pid + " to stop.", timeout, () -> waitWhileIsAlive(pid, timeout, printDots)); if (ProcessUtils.isAlive(pid)) { throw new CommandException("Timed out waiting for the server process to stop."); } } if (adminAddress == null) { - return; + return portTimeout; } final boolean portIsFree = waitWhileListening(adminAddress, portTimeout, printDots); if (portIsFree) { - return; + return timeout == null ? null : timeout.minus(Duration.between(start, Instant.now())); } - throw new CommandException("Timed out waiting for the server to stop."); + throw new CommandException("Timed out waiting for the server to stop after " + timeout.toMillis() + " ms"); } /** @@ -416,8 +417,6 @@ protected final String waitForStart(final Long oldPid, final ServerLifeSignCheck LOG.log(Level.WARNING, "The endpoint is alive, but we failed to reset the local password.", e); } - LOG.log(INFO, () -> "Waiting until start of " + lifeSignCheck.getServerTitleAndName() + " completes."); - final ServerLifeSignChecker checker = new ServerLifeSignChecker(lifeSignCheck, pidFile, adminEndpointsSupplier, printDots); final GlassFishProcess process = GlassFishProcess.of(pid); final ServerLifeSigns signs = checker.watchStartup(process, startTimeout); @@ -455,7 +454,7 @@ private String report(ServerLifeSigns signs) { report.append('\n').append(restartLog); } } - return report.toString(); + return report.append('\n').toString(); } private String loadRestartLog(final File logFile ) { diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java index 4893dc60662..8e9702118c5 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java @@ -36,8 +36,8 @@ import static com.sun.enterprise.admin.cli.CLIConstants.DEATH_TIMEOUT_MS; import static com.sun.enterprise.admin.cli.CLIConstants.WAIT_FOR_DAS_TIME_MS; -import static com.sun.enterprise.admin.servermgmt.cli.ServerLifeSignChecker.step; import static com.sun.enterprise.admin.servermgmt.cli.StartServerHelper.parseCustomEndpoints; +import static com.sun.enterprise.admin.servermgmt.util.CommandAction.step; /** * THe restart-domain command. The local portion of this command is only used to block until: @@ -112,6 +112,10 @@ protected void doCommand() throws CommandException { startTimeout = timeout; } + // Well, this looks duplicit, but it is not. The port watcher starts before we run the command + // on the instance. It will attach to the admin endpoint and wait for the disconnection. + // Meanwhile we ask the instance for restart. + // After we are sure that the server stopped, we will monitor its start. if (portWatcher != null && !portWatcher.get(startTimeout)) { logger.warning("The endpoint is still listening after timeout: " + oldAdminAddress); } diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignChecker.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignChecker.java index 110db52f6fc..ad67b53c9fa 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignChecker.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignChecker.java @@ -16,6 +16,7 @@ package com.sun.enterprise.admin.servermgmt.cli; +import com.sun.enterprise.admin.servermgmt.util.CommandAction; import com.sun.enterprise.universal.process.ProcessUtils; import com.sun.enterprise.util.HostAndPort; @@ -23,13 +24,12 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.time.Duration; -import java.time.Instant; import java.util.List; import java.util.function.Supplier; import org.glassfish.api.admin.CommandException; -import static java.lang.System.Logger.Level.DEBUG; +import static com.sun.enterprise.admin.servermgmt.util.CommandAction.step; public class ServerLifeSignChecker { private static final Logger LOG = System.getLogger(ServerLifeSignChecker.class.getName()); @@ -55,41 +55,47 @@ public ServerLifeSignChecker(ServerLifeSignCheck checks, File pidFile, Supplier< * @param process * @param timeout * @return true if we can make the final decision. + * @throws CommandException */ - public ServerLifeSigns watchStartup(GlassFishProcess process, Duration timeout) { + public ServerLifeSigns watchStartup(GlassFishProcess process, Duration timeout) throws CommandException { final ServerLifeSigns signs = new ServerLifeSigns(); - if (timeout != null && timeout.isNegative()) { - signs.situationReport = createSituationReport(process); - return createTimeoutReport(signs); - } - if (!checks.isPidFile() && !checks.isProcessAlive() && !checks.isAdminEndpoint() && !checks.isCustomEndpoints()) { - signs.summary = "All checks of the server state were disabled. Assuming the server is running."; + final CommandAction action = () -> { + if (timeout != null && timeout.isNegative()) { + signs.situationReport = createSituationReport(process); + createTimeoutReport(signs); + return; + } + if (!checks.isPidFile() && !checks.isProcessAlive() && !checks.isAdminEndpoint() && !checks.isCustomEndpoints()) { + signs.summary = "All checks of the server state were disabled. Assuming the server is running."; + signs.situationReport = createSituationReport(process); + signs.suggestion = getSuggestions(); + return; + } + final boolean wasTimeout = !waitFor(process, timeout); signs.situationReport = createSituationReport(process); + if (wasTimeout) { + createTimeoutReport(signs); + return; + } + if (process.isAlive()) { + signs.summary = "Successfully started the " + checks.getServerTitleAndName() + "."; + return; + } + signs.error = true; signs.suggestion = getSuggestions(); - return signs; - } - final boolean wasTimeout = !waitFor(process, timeout); - signs.situationReport = createSituationReport(process); - if (wasTimeout) { - return createTimeoutReport(signs); - } - if (process.isAlive()) { - signs.summary = "Successfully started the " + checks.getServerTitleAndName() + "."; - return signs; - } - signs.error = true; - signs.suggestion = getSuggestions(); - final Integer exitCode = process.exitCode(); - if (exitCode == null) { - signs.summary = "The process died."; - } else { - signs.summary = "The startup command return code was " + exitCode + " which means that the start "; - if (exitCode == 0) { - signs.summary += "succeded, however later the process stopped for some reason."; + final Integer exitCode = process.exitCode(); + if (exitCode == null) { + signs.summary = "The process died."; } else { - signs.summary += "failed."; + signs.summary = "The startup command return code was " + exitCode + " which means that the start "; + if (exitCode == 0) { + signs.summary += "succeded, however later the process stopped for some reason."; + } else { + signs.summary += "failed."; + } } - } + }; + step("Waiting until start of " + checks.getServerTitleAndName() + " completes.", timeout, action); return signs; } @@ -178,7 +184,8 @@ private String createSituationReport(GlassFishProcess process) { private void appendEndpoints(List endpoints, final StringBuilder report) { for (HostAndPort endpoint : endpoints) { final boolean listening = ProcessUtils.isListening(endpoint); - report.append("\n ").append(endpoint.getHost()).append(':').append(endpoint.getPort()); + report.append("\n ").append(endpoint.isSecure() ? "https://" : "http://").append(endpoint.getHost()) + .append(':').append(endpoint.getPort()); report.append(' ').append(listening ? "is" : "is not").append(" reachable."); } } @@ -218,25 +225,6 @@ private boolean isListeningOnAllEndpoints(List endpoints) { return true; } - public static Duration step(String message, Duration timeout, Action action) throws CommandException { - if (timeout != null && timeout.isNegative()) { - return timeout; - } - if (message != null) { - LOG.log(DEBUG, message); - } - Instant start = Instant.now(); - action.action(); - Duration stopDuration = Duration.between(start, Instant.now()); - return timeout == null ? null : timeout.minus(stopDuration); - } - - @FunctionalInterface - public interface Action { - void action() throws CommandException; - } - - public static final class ServerLifeSigns { private boolean error; private String summary; diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartDomainCommand.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartDomainCommand.java index bac0cd0ce07..676deb421b5 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartDomainCommand.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartDomainCommand.java @@ -28,6 +28,7 @@ import com.sun.enterprise.util.HostAndPort; import com.sun.enterprise.util.ObjectAnalyzer; +import java.io.File; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; @@ -192,7 +193,7 @@ protected int executeCommand() throws CommandException { @Override public final GFLauncher createLauncher() throws GFLauncherException, MiniXmlParserException, CommandException { final GFLauncher gfLauncher = GFLauncherFactory.getInstance(getType()); - launchParameters = gfLauncher.getInfo(); + launchParameters = gfLauncher.getParameters(); launchParameters.setDomainName(getDomainName()); launchParameters.setDomainParentDir(getDomainsDir().getPath()); launchParameters.setVerbose(verbose || upgrade); @@ -284,7 +285,7 @@ private int waitForAutoUpgradeFinish() { * NOTE: this depends on glassFishLauncher.setup having already been called. */ private void doAdminPasswordCheck() throws CommandException { - String adminRealmKeyFile = launcher.getAdminRealmKeyFile(); + File adminRealmKeyFile = launcher.getAdminRealmKeyFile(); if (adminRealmKeyFile == null) { return; } diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java index 7cbede62037..9fce0a4a501 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java @@ -69,7 +69,7 @@ public final class StartServerHelper { private final boolean terse; private final GFLauncher launcher; private final File pidFile; - private final GFLauncherInfo info; + private final GFLauncherInfo launchParams; private final List adminAddresses; private final ServerDirs serverDirs; private final String serverTitleAndName; @@ -79,9 +79,9 @@ public StartServerHelper(boolean terse, Duration timeout, ServerDirs serverDirs, ServerLifeSignCheck lifeSignCheck) throws GFLauncherException { this.terse = terse; this.launcher = launcher; - this.info = launcher.getInfo(); - this.serverTitleAndName = (info.isDomain() ? "domain " : "instance ") + serverDirs.getServerName(); - this.adminAddresses = info.getAdminAddresses(); + this.launchParams = launcher.getParameters(); + this.serverTitleAndName = (launchParams.isDomain() ? "domain " : "instance ") + serverDirs.getServerName(); + this.adminAddresses = launchParams.getAdminAddresses(); this.serverDirs = serverDirs; this.pidFile = serverDirs.getPidFile(); this.lifeSignCheck = lifeSignCheck; @@ -94,7 +94,7 @@ public StartServerHelper(boolean terse, Duration timeout, ServerDirs serverDirs, configureLoggingOfRestart(serverDirs.getRestartLogFile()); } checkFreeDebugPort(launcher.getDebugPort(), Duration.ofSeconds(10L), terse); - checkFreeAdminPorts(info.getAdminAddresses()); + checkFreeAdminPorts(launchParams.getAdminAddresses()); deletePidFile(); } @@ -114,11 +114,11 @@ public int talkWithUser() throws GFLauncherException, MiniXmlParserException { break; case RESTART_DEBUG_ON: LOG.log(INFO, "restartChangeDebug", "on"); - info.setDebug(true); + launchParams.setDebug(true); break; case RESTART_DEBUG_OFF: LOG.log(INFO, "restartChangeDebug", "off"); - info.setDebug(false); + launchParams.setDebug(false); break; default: return returnValue; @@ -129,16 +129,20 @@ public int talkWithUser() throws GFLauncherException, MiniXmlParserException { } } - public String waitForServerStart(Duration timeout) throws GFLauncherException { + public String waitForServerStart(Duration timeout) throws CommandException { if (!terse) { - System.out.print("Waiting for " + serverTitleAndName + " to start "); + if (launcher.isSuspendEnabled()) { + // If the server starts suspended, user needs to see this before it happens. + System.out.print("Debugging is configured to listen on port " + launcher.getDebugPort() + + ". Server's JVM is set to suspend."); + } } final GlassFishProcess glassFishProcess = GlassFishProcess.of(launcher.getProcess()); final ServerLifeSignChecker checker = new ServerLifeSignChecker(lifeSignCheck, pidFile, () -> adminAddresses, !terse); final ServerLifeSigns signs = checker.watchStartup(glassFishProcess, timeout); final String report = report(signs); if (signs.isError()) { - throw new GFLauncherException(report); + throw new CommandException(report); } return report; } @@ -151,7 +155,14 @@ private String report(ServerLifeSigns signs) { report.append('\n').append(signs.getSuggestion()); } report.append("\n Location: ").append(serverDirs.getServerDir()); - report.append("\n Log File: ").append(launcher.getLogFilename()); + report.append("\n Log File: ").append(launcher.getLogFile()); + if (launcher.getDebugPort() != null) { + report.append("\n Debugging is configured to listen on port " + launcher.getDebugPort() + "."); + if (launcher.isSuspendEnabled()) { + report.append(" Server's JVM is set to suspend."); + } + } + final String situationReport = signs.getSituationReport(); if (situationReport != null) { report.append(signs.getSituationReport()); diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/domain/DomainSecurity.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/domain/DomainSecurity.java index 1ea18914083..ce73f3973f0 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/domain/DomainSecurity.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/domain/DomainSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -43,8 +43,7 @@ public class DomainSecurity extends MasterPasswordFileManager { * @param password Password. */ void processAdminKeyFile(File keyFile, String user, String password, final String[] adminUserGroups) throws IOException { - final String keyFilePath = keyFile.getAbsolutePath(); - final FileRealmHelper fileRealm = new FileRealmHelper(keyFilePath); + final FileRealmHelper fileRealm = new FileRealmHelper(keyFile.getAbsoluteFile()); final String[] group = adminUserGroups; fileRealm.addUser(user, password.toCharArray(), group); fileRealm.persist(); diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/util/CommandAction.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/util/CommandAction.java new file mode 100644 index 00000000000..8d339bc3574 --- /dev/null +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/util/CommandAction.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 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 com.sun.enterprise.admin.servermgmt.util; + +import java.time.Duration; +import java.time.Instant; + +import org.glassfish.api.admin.CommandException; + +/** + * Action to be executed by {@link #step(String, Duration, CommandAction)} and wraoped by a simple time + * monitoring report. + * Example of the standard output: + *
+ * Waiting until start of domain domain1 completes... finished after 1488 ms
+ * 
+ */ +@FunctionalInterface +public interface CommandAction { + + /** + * Action implementation. + * Use {@link #step(String, Duration, CommandAction)} to execute the action. + * + * @throws CommandException + */ + void action() throws CommandException; + + /** + * Action to be executed by {@link #step(String, Duration, CommandAction)} and wraoped by a simple time + * monitoring report. + * Example of the standard output: + *
+     * Waiting until start of domain domain1 completes... finished after 1488 ms
+     * 
+ * @param message The message will be printed before the action. + * @param timeout can be null + * @param action action to be executed + * @return remaining timeout. + * @throws CommandException + */ + static Duration step(String message, Duration timeout, CommandAction action) throws CommandException { + if (timeout != null && timeout.isNegative()) { + return timeout; + } + if (message != null) { + System.out.print(message); + } + Instant start = Instant.now(); + action.action(); + Duration stopDuration = Duration.between(start, Instant.now()); + if (message != null) { + System.out.println(" ... finished after " + stopDuration.toMillis() + " ms."); + } + return timeout == null ? null : timeout.minus(stopDuration); + } +} diff --git a/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/RestartLocalInstanceCommand.java b/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/RestartLocalInstanceCommand.java index 341f6233e49..158ab55358d 100644 --- a/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/RestartLocalInstanceCommand.java +++ b/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/RestartLocalInstanceCommand.java @@ -37,7 +37,7 @@ import static com.sun.enterprise.admin.cli.CLIConstants.DEATH_TIMEOUT_MS; import static com.sun.enterprise.admin.cli.CLIConstants.WAIT_FOR_DAS_TIME_MS; -import static com.sun.enterprise.admin.servermgmt.cli.ServerLifeSignChecker.step; +import static com.sun.enterprise.admin.servermgmt.util.CommandAction.step; /** * @author Byron Nevins @@ -81,6 +81,10 @@ protected final void doCommand() throws CommandException { startTimeout = timeout; } + // Well, this looks duplicit, but it is not. The port watcher starts before we run the command + // on the instance. It will attach to the admin endpoint and wait for the disconnection. + // Meanwhile we ask the instance for restart. + // After we are sure that the server stopped, we will monitor its start. if (portWatcher != null && !portWatcher.get(startTimeout)) { logger.warning("The endpoint is still listening after timeout: " + oldAdminAddress); } diff --git a/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/StartLocalInstanceCommand.java b/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/StartLocalInstanceCommand.java index 0ac8adb2259..5ad1fd030f2 100644 --- a/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/StartLocalInstanceCommand.java +++ b/nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/StartLocalInstanceCommand.java @@ -165,7 +165,7 @@ protected int executeCommand() throws CommandException { @Override public final GFLauncher createLauncher() throws GFLauncherException, MiniXmlParserException, CommandException { final GFLauncher gfLauncher = GFLauncherFactory.getInstance(getType()); - this.launchParameters = gfLauncher.getInfo(); + this.launchParameters = gfLauncher.getParameters(); launchParameters.setInstanceName(instanceName); launchParameters.setInstanceRootDir(instanceDir); launchParameters.setVerbose(verbose); diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/universal/process/ProcessUtils.java b/nucleus/common/common-util/src/main/java/com/sun/enterprise/universal/process/ProcessUtils.java index 18209db42c7..ba22b686fb0 100644 --- a/nucleus/common/common-util/src/main/java/com/sun/enterprise/universal/process/ProcessUtils.java +++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/universal/process/ProcessUtils.java @@ -204,7 +204,7 @@ public static boolean waitWhileListening(HostAndPort endpoint, Duration timeout, final boolean result = action.get(); DotPrinter.stopWaiting(dotPrinter); final long millis = Duration.between(start, Instant.now()).toMillis(); - LOG.log(result ? DEBUG : WARNING, () -> "Waiting finished after " + millis + " ms. Endpoint " + endpoint + LOG.log(DEBUG, () -> "Waiting finished after " + millis + " ms. Endpoint " + endpoint + (result ? " stopped" : " did not stop") + " listening."); return result; } @@ -302,7 +302,7 @@ public static boolean waitFor(Supplier sign, Duration timeout, boolean final Boolean result = action.get(); DotPrinter.stopWaiting(dotPrinter); final long millis = Duration.between(start, Instant.now()).toMillis(); - LOG.log(result ? DEBUG : INFO, + LOG.log(DEBUG, () -> "Waiting finished after " + millis + " ms. Action " + (result ? "succeeded." : "timed out.")); return result; } @@ -394,7 +394,6 @@ public void run() { System.out.flush(); } } catch (InterruptedException e) { - System.out.println(); Thread.currentThread().interrupt(); } } diff --git a/nucleus/common/common-util/src/main/java/org/glassfish/security/common/FileRealmHelper.java b/nucleus/common/common-util/src/main/java/org/glassfish/security/common/FileRealmHelper.java index b1da4a59871..7f48adc0c81 100644 --- a/nucleus/common/common-util/src/main/java/org/glassfish/security/common/FileRealmHelper.java +++ b/nucleus/common/common-util/src/main/java/org/glassfish/security/common/FileRealmHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -113,13 +113,12 @@ public final class FileRealmHelper * @exception IOException If the configuration parameters * identify a corrupt keyfile */ - public FileRealmHelper(String keyfileName) throws IOException - { - keyfile = new File(keyfileName); + public FileRealmHelper(File keyfile) throws IOException { + this.keyfile = keyfile; // if not existent, try to create if (!keyfile.exists()) { if (keyfile.createNewFile() == false) { - throw new IOException(sm.getString("filerealm.badwrite", keyfileName)); + throw new IOException(sm.getString("filerealm.badwrite", keyfile)); } } loadKeyFile(); diff --git a/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/file/FileRealm.java b/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/file/FileRealm.java index 718d945f439..2d683476761 100644 --- a/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/file/FileRealm.java +++ b/nucleus/security/core/src/main/java/com/sun/enterprise/security/auth/realm/file/FileRealm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation. + * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -27,6 +27,7 @@ import com.sun.enterprise.security.auth.realm.exceptions.NoSuchUserException; import com.sun.enterprise.security.util.IASSecurityException; +import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; @@ -191,10 +192,13 @@ protected void init(Properties props) throws BadRealmException, NoSuchRealmExcep _logger.log(FINE, "FileRealm : " + JAAS_CONTEXT_PARAM + "={0}", jaasCtx); try { + final File realmFile; if (isEmbeddedServer()) { - file = writeConfigFileToTempDir(file).getAbsolutePath(); + realmFile = writeConfigFileToTempDir(file).getAbsoluteFile(); + } else { + realmFile = new File(file); } - fileRealmHelper = new FileRealmHelper(file); + fileRealmHelper = new FileRealmHelper(realmFile); } catch (IOException ioe) { throw new BadRealmException(MessageFormat.format("Unable to create keyfile: {0}", ioe.toString())); } From 5a49bf605a7fcc9cdf3a7abc061499c5c3d63224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mat=C4=9Bj=C4=8Dek?= Date: Fri, 28 Nov 2025 01:17:40 +0100 Subject: [PATCH 2/5] Fixed links to the JPDA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Matějček --- docs/reference-manual/src/main/asciidoc/copy-config.adoc | 6 ++++-- docs/reference-manual/src/main/asciidoc/create-cluster.adoc | 4 +--- docs/reference-manual/src/main/asciidoc/create-domain.adoc | 4 +--- .../reference-manual/src/main/asciidoc/create-instance.adoc | 3 +-- .../src/main/asciidoc/create-local-instance.adoc | 3 +-- docs/reference-manual/src/main/asciidoc/restart-domain.adoc | 4 +--- .../src/main/asciidoc/restart-instance.adoc | 4 +--- .../src/main/asciidoc/restart-local-instance.adoc | 4 +--- docs/reference-manual/src/main/asciidoc/start-instance.adoc | 4 +--- .../src/main/asciidoc/start-local-instance.adoc | 4 +--- 10 files changed, 13 insertions(+), 27 deletions(-) diff --git a/docs/reference-manual/src/main/asciidoc/copy-config.adoc b/docs/reference-manual/src/main/asciidoc/copy-config.adoc index 87ad2932c09..86e52df475b 100644 --- a/docs/reference-manual/src/main/asciidoc/copy-config.adoc +++ b/docs/reference-manual/src/main/asciidoc/copy-config.adoc @@ -79,8 +79,10 @@ asadmin-options:: requires superuser privileges. `JAVA_DEBUGGER_PORT`;; This property specifies the port number of the port that is used for - connections to the Java Platform Debugger Architecture (JPDA) - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + connections to the Specifies whether the domain is restarted with + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] + debugging enabled. + debugger. Valid values are 1-65535. On UNIX, creating sockets that listen on ports 1-1024 requires superuser privileges. `JMS_PROVIDER_PORT`;; diff --git a/docs/reference-manual/src/main/asciidoc/create-cluster.adoc b/docs/reference-manual/src/main/asciidoc/create-cluster.adoc index 57580689b37..4eeee72e5fd 100644 --- a/docs/reference-manual/src/main/asciidoc/create-cluster.adoc +++ b/docs/reference-manual/src/main/asciidoc/create-cluster.adoc @@ -130,9 +130,7 @@ asadmin-options:: `JAVA_DEBUGGER_PORT`;; This property specifies the port number of the port that is used for connections to the - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture (JPDA)] - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugger. Valid values are 1-65535. On UNIX, creating sockets that listen on ports 1-1024 requires superuser privileges. `JMS_PROVIDER_PORT`;; diff --git a/docs/reference-manual/src/main/asciidoc/create-domain.adoc b/docs/reference-manual/src/main/asciidoc/create-domain.adoc index a00b5182c08..ccf9234358e 100644 --- a/docs/reference-manual/src/main/asciidoc/create-domain.adoc +++ b/docs/reference-manual/src/main/asciidoc/create-domain.adoc @@ -211,9 +211,7 @@ When the `--portbase` option is specified, the output of this `java.debugger.port`;; This property specifies the port number of the port that is used for connections to the - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture (JPDA)] - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugger. Valid values are 1-65535. On UNIX, creating sockets that listen on ports 1-1024 requires superuser privileges. `jms.port`;; diff --git a/docs/reference-manual/src/main/asciidoc/create-instance.adoc b/docs/reference-manual/src/main/asciidoc/create-instance.adoc index 34113463b74..ae138901f0b 100644 --- a/docs/reference-manual/src/main/asciidoc/create-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/create-instance.adoc @@ -212,8 +212,7 @@ When the `--portbase` option is specified, the output of this requires superuser privileges. `JAVA_DEBUGGER_PORT`;; This property specifies the port number of the port that is used for - connections to the Java Platform Debugger Architecture (JPDA) - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + connections to the https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugger. Valid values are 1-65535. On UNIX, creating sockets that listen on ports 1-1024 requires superuser privileges. `JMS_PROVIDER_PORT`;; diff --git a/docs/reference-manual/src/main/asciidoc/create-local-instance.adoc b/docs/reference-manual/src/main/asciidoc/create-local-instance.adoc index 15cfd2e3741..142cce58e28 100644 --- a/docs/reference-manual/src/main/asciidoc/create-local-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/create-local-instance.adoc @@ -266,8 +266,7 @@ When the `--portbase` option is specified, the output of this requires superuser privileges. `JAVA_DEBUGGER_PORT`;; This property specifies the port number of the port that is used for - connections to the Java Platform Debugger Architecture (JPDA) - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + connections to the https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugger. Valid values are 1-65535. On UNIX, creating sockets that listen on ports 1-1024 requires superuser privileges. `JMS_PROVIDER_PORT`;; diff --git a/docs/reference-manual/src/main/asciidoc/restart-domain.adoc b/docs/reference-manual/src/main/asciidoc/restart-domain.adoc index 3834873ab77..ea26eaa3b2a 100644 --- a/docs/reference-manual/src/main/asciidoc/restart-domain.adoc +++ b/docs/reference-manual/src/main/asciidoc/restart-domain.adoc @@ -55,9 +55,7 @@ asadmin-options:: Displays the help text for the subcommand. `--debug`:: Specifies whether the domain is restarted with - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture (JPDA)] - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugging enabled. Possible values are as follows: diff --git a/docs/reference-manual/src/main/asciidoc/restart-instance.adoc b/docs/reference-manual/src/main/asciidoc/restart-instance.adoc index a72415a5eb1..fe11637feca 100644 --- a/docs/reference-manual/src/main/asciidoc/restart-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/restart-instance.adoc @@ -78,9 +78,7 @@ asadmin-options:: Displays the help text for the subcommand. `--debug`:: Specifies whether the instance is restarted with - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture - (JPDA)](https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugging enabled. Possible values are as follows: `true`;; diff --git a/docs/reference-manual/src/main/asciidoc/restart-local-instance.adoc b/docs/reference-manual/src/main/asciidoc/restart-local-instance.adoc index b4ba148bacb..41fd05aad92 100644 --- a/docs/reference-manual/src/main/asciidoc/restart-local-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/restart-local-instance.adoc @@ -82,9 +82,7 @@ asadmin-options:: Displays the help text for the subcommand. `--debug`:: Specifies whether the instance is restarted with - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture (JPDA)] - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugging enabled. Possible values are as follows: diff --git a/docs/reference-manual/src/main/asciidoc/start-instance.adoc b/docs/reference-manual/src/main/asciidoc/start-instance.adoc index 98af6ca2f8a..15d8751a628 100644 --- a/docs/reference-manual/src/main/asciidoc/start-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/start-instance.adoc @@ -59,9 +59,7 @@ asadmin-options:: Displays the help text for the subcommand. `--debug`:: Specifies whether the instance is started with - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture (JPDA)] - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugging enabled. + Possible values are as follows: diff --git a/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc b/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc index fb0df891dc0..4ae2e4f237f 100644 --- a/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc @@ -64,9 +64,7 @@ asadmin-options:: `--debug`:: `-d`:: Specifies whether the instance is started with - http://java.sun.com/javase/technologies/core/toolsapis/jpda/[Java - Platform Debugger Architecture (JPDA)] - (https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html) + https://docs.oracle.com/en/java/javase/17/docs/specs/jpda/jpda.html[Java Platform Debugger Architecture (JPDA)] debugging enabled. + Possible values are as follows: From 97f27446527b65df158a71e1245e45c6b59fe80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mat=C4=9Bj=C4=8Dek?= Date: Fri, 28 Nov 2025 02:02:49 +0100 Subject: [PATCH 3/5] Added documentation for the new property --server-output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - other are rather experimental, so I will not document them for now Signed-off-by: David Matějček --- .../src/main/asciidoc/restart-domain.adoc | 12 +++++++++ .../src/main/asciidoc/start-domain.adoc | 12 +++++++++ .../main/asciidoc/start-local-instance.adoc | 14 +++++++++++ .../admin/servermgmt/cli/restart-domain.1 | 13 ++++++++++ .../admin/servermgmt/cli/start-domain.1 | 25 ++++++++++++++----- .../v3/admin/cluster/start-instance.1 | 8 +++--- .../admin/cli/cluster/start-local-instance.1 | 13 ++++++++++ 7 files changed, 87 insertions(+), 10 deletions(-) diff --git a/docs/reference-manual/src/main/asciidoc/restart-domain.adoc b/docs/reference-manual/src/main/asciidoc/restart-domain.adoc index ea26eaa3b2a..104c94ecec0 100644 --- a/docs/reference-manual/src/main/asciidoc/restart-domain.adoc +++ b/docs/reference-manual/src/main/asciidoc/restart-domain.adoc @@ -23,6 +23,7 @@ asadmin restart-domain [--force[=]] [--help|-?] [--kill[=]] +[--server-output|-o=]] [--timeout ] [domain_name] ---- @@ -95,6 +96,17 @@ The default is the current setting of this option for the domain that The domain is killed. The subcommand uses functionality of the operating system to terminate the domain process. +`--server-output`:: +`-o`:: + Specifies if the command should or should not print the server output. + If not set, server prints the output just when the command failed. + Possible values are as follows: + + `true`;; + The output of the server startup will be printed after the command finishes. + `false`;; + The output of the server startup will not be printed. + `--timeout`:: Specifies timeout in seconds to evaluate the expected result. If the timeout is exceeded, the command fails - however it does diff --git a/docs/reference-manual/src/main/asciidoc/start-domain.adoc b/docs/reference-manual/src/main/asciidoc/start-domain.adoc index f1f25eeaa28..d1f34380c37 100644 --- a/docs/reference-manual/src/main/asciidoc/start-domain.adoc +++ b/docs/reference-manual/src/main/asciidoc/start-domain.adoc @@ -23,6 +23,7 @@ asadmin start-domain [--drop-interrupted-commands[=]] [--dry-run|-n[=]] [--help|-?] +[--server-output|-o=]] [--suspend|-s[=]] [--timeout ] [--upgrade[=]] @@ -101,6 +102,17 @@ asadmin-options:: JVM options and when troubleshooting startup issues. + The default value is `false`. +`--server-output`:: +`-o`:: + Specifies if the command should or should not print the server output. + If not set, server prints the output just when the command failed. + Possible values are as follows: + + `true`;; + The output of the server startup will be printed after the command finishes. + `false`;; + The output of the server startup will not be printed. + `--suspend`:: `-s`:: Specifies whether the domain is started with diff --git a/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc b/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc index 4ae2e4f237f..7f533d9dce7 100644 --- a/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc +++ b/docs/reference-manual/src/main/asciidoc/start-local-instance.adoc @@ -23,6 +23,7 @@ asadmin start-local-instance [--help|-?] [--node ] [--nodedir ] +[--server-output|-o=]] [--sync ] [--timeout ] [--verbose|-v[=]] @@ -74,6 +75,7 @@ Possible values are as follows: number for JPDA debugging is displayed. `false`;; The instance is started with JPDA debugging disabled (default). + `--dry-run`:: `-n`:: Suppresses actual starting of the instance. Instead, @@ -90,6 +92,18 @@ Possible values are as follows: Specifies the directory that contains the instance's node directory. The instance's files are stored in the instance's node directory. The default is as-install``/nodes``. + +`--server-output`:: +`-o`:: + Specifies if the command should or should not print the server output. + If not set, server prints the output just when the command failed. + Possible values are as follows: + + `true`;; + The output of the server startup will be printed after the command finishes. + `false`;; + The output of the server startup will not be printed. + `--sync`:: The type of synchronization between the DAS and the instance's files when the instance is started. + diff --git a/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/restart-domain.1 b/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/restart-domain.1 index c3393aff1d8..3e9edb3a023 100644 --- a/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/restart-domain.1 +++ b/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/restart-domain.1 @@ -10,6 +10,7 @@ SYNOPSIS [--force[=]] [--help|-?] [--kill[=]] + [--server-output|-o=]] [--timeout ] [domain_name] @@ -89,6 +90,18 @@ OPTIONS The domain is killed. The subcommand uses functionality of the operating system to terminate the domain process. + --server-output, -o + Specifies if the command should or should not print the server output. + If not set, server prints the output just when the command failed. + + Possible values are as follows: + + true + The output of the server startup will be printed after the command finishes. + + false + The output of the server startup will not be printed. + --timeout Specifies timeout in seconds to evaluate the expected result. If the timeout is exceeded, the command fails - however it does diff --git a/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/start-domain.1 b/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/start-domain.1 index 109d9730ddc..1aaaa618c2b 100644 --- a/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/start-domain.1 +++ b/nucleus/admin/server-mgmt/src/main/manpages/com/sun/enterprise/admin/servermgmt/cli/start-domain.1 @@ -10,6 +10,7 @@ SYNOPSIS [--drop-interrupted-commands[=]] [--dry-run|-n[=]] [--help|-?] + [--server-output|-o=]] [--suspend|-s[=]] [--timeout ] [--upgrade[=]] @@ -97,6 +98,18 @@ OPTIONS --help, -? Displays the help text for the subcommand. + --server-output, -o + Specifies if the command should or should not print the server output. + If not set, server prints the output just when the command failed. + + Possible values are as follows: + + true + The output of the server startup will be printed after the command finishes. + + false + The output of the server startup will not be printed. + --suspend, -s Specifies whether the domain is started with Java Platform Debugger Architecture (JPDA) debugging enabled and suspends the newly started VM @@ -106,18 +119,18 @@ OPTIONS Possible values are as follows: - true + true The instance is started with JPDA debugging enabled and a suspend policy of `SUSPEND_ALL`. The port number for JPDA debugging is displayed. - false + false The instance is started with JPDA debugging disabled (default). --timeout - Specifies timeout in seconds to evaluate the expected result. - If the timeout is exceeded, the command fails - however it does - not mean it did not make any changes. The domain status is unknown - in such case. + Specifies timeout in seconds to evaluate the expected result. + If the timeout is exceeded, the command fails - however it does + not mean it did not make any changes. The domain status is unknown + in such case. --upgrade Specifies whether the configuration of the domain administration diff --git a/nucleus/cluster/admin/src/main/manpages/com/sun/enterprise/v3/admin/cluster/start-instance.1 b/nucleus/cluster/admin/src/main/manpages/com/sun/enterprise/v3/admin/cluster/start-instance.1 index 6fed5a72dc9..656aa1a1997 100644 --- a/nucleus/cluster/admin/src/main/manpages/com/sun/enterprise/v3/admin/cluster/start-instance.1 +++ b/nucleus/cluster/admin/src/main/manpages/com/sun/enterprise/v3/admin/cluster/start-instance.1 @@ -99,10 +99,10 @@ OPTIONS the instance's directories. --timeout - Specifies timeout in seconds to evaluate the expected result. - If the timeout is exceeded, the command fails - however it does - not mean it did not make any changes. The instance status is unknown - in such case. + Specifies timeout in seconds to evaluate the expected result. + If the timeout is exceeded, the command fails - however it does + not mean it did not make any changes. The instance status is unknown + in such case. OPERANDS instance-name diff --git a/nucleus/cluster/cli/src/main/manpages/com/sun/enterprise/admin/cli/cluster/start-local-instance.1 b/nucleus/cluster/cli/src/main/manpages/com/sun/enterprise/admin/cli/cluster/start-local-instance.1 index 441205578ac..b627f2c929e 100644 --- a/nucleus/cluster/cli/src/main/manpages/com/sun/enterprise/admin/cli/cluster/start-local-instance.1 +++ b/nucleus/cluster/cli/src/main/manpages/com/sun/enterprise/admin/cli/cluster/start-local-instance.1 @@ -11,6 +11,7 @@ SYNOPSIS [--help|-?] [--node ] [--nodedir ] + [--server-output|-o=]] [--sync ] [--timeout ] [--verbose|-v[=]] @@ -77,6 +78,18 @@ OPTIONS directory. The instance's files are stored in the instance's node directory. The default is as-install/nodes. + --server-output, -o + Specifies if the command should or should not print the server output. + If not set, server prints the output just when the command failed. + + Possible values are as follows: + + true + The output of the server startup will be printed after the command finishes. + + false + The output of the server startup will not be printed. + --sync The type of synchronization between the DAS and the instance's files when the instance is started. From d1d29306cf6ebd825f00580ba263bdfc7396d7d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mat=C4=9Bj=C4=8Dek?= Date: Fri, 28 Nov 2025 02:03:19 +0100 Subject: [PATCH 4/5] Fixed printing after domain restart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Matějček --- .../admin/servermgmt/cli/LocalServerCommand.java | 6 +++--- .../admin/servermgmt/cli/RestartDomainCommand.java | 1 - .../admin/servermgmt/cli/ServerLifeSignCheck.java | 8 ++++++++ .../admin/servermgmt/cli/StartServerHelper.java | 4 +--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java index 8ce9e88797c..9a6ff915769 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java @@ -420,7 +420,7 @@ protected final String waitForStart(final Long oldPid, final ServerLifeSignCheck final ServerLifeSignChecker checker = new ServerLifeSignChecker(lifeSignCheck, pidFile, adminEndpointsSupplier, printDots); final GlassFishProcess process = GlassFishProcess.of(pid); final ServerLifeSigns signs = checker.watchStartup(process, startTimeout); - final String report = report(signs); + final String report = report(signs, lifeSignCheck.isPrintServerOutput(signs.isError())); if (signs.isError()) { throw new CommandException(report); } @@ -437,7 +437,7 @@ private String reportPidFileIssue(final File pidFile) { return error + "\n" + restartLog; } - private String report(ServerLifeSigns signs) { + private String report(ServerLifeSigns signs, boolean printOutput) { final StringBuilder report = new StringBuilder(2048); report.append('\n').append(signs.getSummary()); if (signs.getSuggestion() != null) { @@ -448,7 +448,7 @@ private String report(ServerLifeSigns signs) { if (situationReport != null) { report.append(signs.getSituationReport()); } - if (signs.isError()) { + if (printOutput) { final String restartLog = loadRestartLog(serverDirs.getRestartLogFile()); if (restartLog != null) { report.append('\n').append(restartLog); diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java index 8e9702118c5..162c314c456 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java @@ -175,7 +175,6 @@ protected int dasNotRunning() throws CommandException { opts.add("--check-admin-port"); opts.add(Boolean.toString(checkAdminEndpoint)); if (printServerOutput != null) { - // TODO: At this moment works just when the domain was not running opts.add("--server-output"); opts.add(Boolean.toString(printServerOutput)); } diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignCheck.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignCheck.java index 7bd643a85f6..a122cd4fe64 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignCheck.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignCheck.java @@ -105,4 +105,12 @@ public boolean isCustomEndpoints() { public List getCustomEndpoints() { return customEndpoints; } + + /** + * @param wasError + * @return if the output should be printed or not. + */ + public boolean isPrintServerOutput(boolean wasError) { + return getPrintServerOutput() == Boolean.TRUE || (wasError && getPrintServerOutput() == null); + } } diff --git a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java index 9fce0a4a501..f2811901c68 100644 --- a/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java +++ b/nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java @@ -169,9 +169,7 @@ private String report(ServerLifeSigns signs) { } // Print output just if user explicitly asked // or start failed and user did not explicitly forbid the print. - if (launcher.getPidBeforeRestart() != null - || lifeSignCheck.getPrintServerOutput() == Boolean.TRUE - || (signs.isError() && lifeSignCheck.getPrintServerOutput() != Boolean.FALSE)) { + if (launcher.getPidBeforeRestart() != null || lifeSignCheck.isPrintServerOutput(signs.isError())) { report.append("\n\n").append(getProcessOutput()); } return report.append('\n').toString(); From 3e820e7b2f2a11c04da68e09750c4fa7dc228bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mat=C4=9Bj=C4=8Dek?= Date: Fri, 28 Nov 2025 02:49:06 +0100 Subject: [PATCH 5/5] Fixed check for duplicated ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Matějček --- appserver/tests/appserv-tests/devtests/ejb/ports.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appserver/tests/appserv-tests/devtests/ejb/ports.java b/appserver/tests/appserv-tests/devtests/ejb/ports.java index 26e14c0f14a..a17b45408d8 100644 --- a/appserver/tests/appserv-tests/devtests/ejb/ports.java +++ b/appserver/tests/appserv-tests/devtests/ejb/ports.java @@ -59,13 +59,13 @@ private static String getFreePort(Set excluded) throws IllegalStateExcep while (true) { counter++; try (ServerSocket socket = new ServerSocket(0)) { - final int port = socket.getLocalPort(); + final String port = Integer.toString(socket.getLocalPort()); socket.setSoTimeout(1); if (excluded.contains(port) && counter >= 20) { throw new IllegalStateException("Cannot open random port, tried 20 times. Port " + port + " is excluded and we were not able to find another."); } - return Integer.toString(port); + return port; } catch (IOException e) { if (counter >= 20) { throw new IllegalStateException("Cannot open random port, tried 20 times.", e);