com.sun.enterprise.v3.admin.RestartServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2021] [Payara Foundation]
package com.sun.enterprise.v3.admin;
import com.sun.enterprise.module.ModulesRegistry;
import com.sun.enterprise.module.bootstrap.StartupContext;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
import com.sun.enterprise.universal.process.JavaClassRunner;
import com.sun.enterprise.universal.process.ProcessUtils;
import com.sun.enterprise.util.StringUtils;
import static com.sun.enterprise.util.StringUtils.ok;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.internal.api.Globals;
import org.glassfish.embeddable.GlassFish;
import java.io.*;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.*;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
/**
* For non-verbose mode: Stop this server, spawn a new JVM that will wait for this JVM to die. The new JVM then starts
* the server again.
*
* For verbose mode: We want the asadmin console itself to do the respawning -- so just return a special int from
* System.exit(). This tells asadmin to restart.
*
* @author Byron Nevins
*/
public class RestartServer {
@Inject
private Provider glassfishProvider;
private final Lock stopLock = new ReentrantLock();
protected final void setDebug(Boolean b) {
debug = b;
}
protected final void setRegistry(final ModulesRegistry registryIn) {
registry = registryIn;
}
protected final void setServerName(String serverNameIn) {
serverName = serverNameIn;
}
/**
* Restart of the application server :
*
* All running services are stopped. LookupManager is flushed.
*
* Client code that started us should notice the special return value and restart us.
*/
protected final void doExecute(AdminCommandContext context) {
try {
// unfortunately we can't rely on constructors with HK2...
if (registry == null) {
throw new NullPointerException(new LocalStringsImpl(getClass()).get("restart.server.internalError", "registry was not set"));
}
init(context);
// get the GlassFish object - we have to wait in case startup is still in progress
// This is a temporary work-around until HK2 supports waiting for the service to
// show up in the ServiceLocator.
GlassFish gfKernel = glassfishProvider.get();
while (gfKernel == null) {
Thread.sleep(1000);
gfKernel = glassfishProvider.get();
}
if (!supervised) {
// do it now while we still have the Logging service running...
reincarnate();
}
prepareToExit();
// else we just return a special int from System.exit()
gfKernel.stop();
} catch (Exception e) {
context.getLogger().severe(strings.get("restart.server.failure", e));
} finally {
stopLock.unlock();
}
}
private void prepareToExit() {
stopLock.lock();
// we need a separate non-deamon thread to perform System.exit, as gfKernel.stop() terminates the only
// non-daemon thread in system, and JVM can terminate before executing System.exit
Thread exitingThread = new Thread("wait-for-restart") {
@Override
public void run() {
stopLock.lock();
int ret = RESTART_NORMAL;
if (debug != null) {
ret = debug ? RESTART_DEBUG_ON : RESTART_DEBUG_OFF;
}
System.exit(ret);
}
};
exitingThread.setDaemon(false);
exitingThread.start();
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////// ALL PRIVATE BELOW ////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
private void init(AdminCommandContext context) throws IOException {
logger = context.getLogger();
props = Globals.get(StartupContext.class).getArguments();
supervised = Boolean.parseBoolean(props.getProperty("-verbose", "false"))
|| Boolean.parseBoolean(props.getProperty("-watchdog", "false"));
logger.info(strings.get("restart.server.init"));
}
private void reincarnate() {
try {
if (restartable()) {
if (setupReincarnationWithAsadmin() || setupReincarnationWithOther()) {
doReincarnation();
} else {
logger.severe(strings.get("restart.server.noStartupInfo",
strings.get("restart.server.asadminError"),
strings.get("restart.server.nonAsadminError")));
}
} else {
logger.severe(strings.get("restart.server.noPasswordFile", passwordFilePath));
}
} catch (RDCException rdce) {
// already logged...
} catch (Exception e) {
logger.severe(strings.get("restart.server.internalError", e));
}
}
private void doReincarnation() throws RDCException {
try {
// TODO JavaClassRunner is very simple and primitive.
// Feel free to beef it up...
String[] props = normalProps;
if (Boolean.parseBoolean(System.getenv("AS_SUPER_DEBUG"))) {
props = debuggerProps; // very very difficult to debug this stuff otherwise!
}
new JavaClassRunner(classpath, props, classname, args);
} catch (Exception e) {
logger.severe(strings.get("restart.server.jvmError", e));
throw new RDCException();
}
}
private boolean setupReincarnationWithAsadmin() throws RDCException {
classpath = props.getProperty("-asadmin-classpath");
classname = props.getProperty("-asadmin-classname");
argsString = props.getProperty("-asadmin-args");
return verify("restart.server.asadminError");
}
private boolean setupReincarnationWithOther() throws RDCException {
classpath = props.getProperty("-startup-classpath");
classname = props.getProperty("-startup-classname");
argsString = props.getProperty("-startup-args");
return verify("restart.server.nonAsadminError");
}
private boolean restartable() throws RDCException {
boolean restartable = true;
File passwordFile;
String asadminString = props.getProperty("-asadmin-args");
if (ok(asadminString) && asadminString.contains("--passwordfile")) {
String[] asadminArgs = asadminString.split(",,,");
for (int i = 0; i < asadminArgs.length; i++) {
if (asadminArgs[i].equals("--passwordfile")) {
if ((i + 1) < asadminArgs.length && ok(asadminArgs[i + 1])) {
passwordFilePath = asadminArgs[i + 1];
break;
}
}
}
} else {
// if there's no password file set, we're done here
return true;
}
passwordFile = new File(passwordFilePath);
if (ok(passwordFilePath)) {
restartable = passwordFile.canRead();
}
return restartable;
}
private boolean verify(String errorStringKey) throws RDCException {
// Either asadmin or non-asadmin startup params have been set -- check them!
// THREE possible returns:
// 1) true
// 2) false
// 3) RDCException
if (classpath == null && classname == null && argsString == null) {
return false;
}
// now that at least one is set -- demand that ALL OF THEM be set...
if (!ok(classpath) || !ok(classname) || argsString == null) {
logger.severe(strings.get(errorStringKey));
throw new RDCException();
}
args = argsString.split(",,,");
handleDebug();
return true;
}
private void handleDebug() {
if (debug == null) // nothing to do!
{
return;
}
stripDebugFromArgs();
stripOperandFromArgs();
int oldlen = args.length;
int newlen = oldlen + 2;
String debugArg = "--debug=" + debug.toString();
String[] newArgs = new String[newlen];
// copy all but the last arg (domain-name)
System.arraycopy(args, 0, newArgs, 0, args.length);
newArgs[newlen - 2] = debugArg;
newArgs[newlen - 1] = serverName;
args = newArgs;
}
private void stripDebugFromArgs() {
// this is surprisingly complex!
// "--debug domain1" is one
// "--debug=true" is one
// "--debug false" is two
boolean twoArgs = false;
int indexOfDebug = -1;
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("--debug=")) {
indexOfDebug = i;
break;
}
if (args[i].startsWith("--debug")) {
indexOfDebug = i;
// who knows what happens in CLI when the domain's name is "true" ?!?
// we could potentially be fooled by that one very unlikely scenario
if (args.length > i + 1) {// broken into two if's for readability...
if (args[i + 1].equals("true") || args[i + 1].equals("false")) {
twoArgs = true;
}
}
break;
}
}
if (indexOfDebug < 0) {
return;
}
int oldlen = args.length;
int newlen = oldlen - 1;
if (twoArgs) {
--newlen;
}
String[] newArgs = new String[newlen];
int ctr = 0;
for (int i = 0; i < oldlen; i++) {
if (i == indexOfDebug) {
continue;
}
if (twoArgs && i == (indexOfDebug + 1)) {
continue;
}
newArgs[ctr++] = args[i];
}
args = newArgs;
}
private void stripOperandFromArgs() {
// remove the domain-name operand
// it may not be here!
if (args.length < 2 || !StringUtils.ok(serverName)) {
return;
}
int newlen = args.length - 1;
if (serverName.equals(args[newlen])) {
String[] newargs = new String[newlen];
System.arraycopy(args, 0, newargs, 0, newlen);
args = newargs;
}
}
private boolean ok(String s) {
return s != null && s.length() > 0;
}
// We use this simply to tell the difference between fatal errors and other
// non-fatal conditions.
private static class RDCException extends Exception {
}
ModulesRegistry registry;
private Boolean debug = null;
private Properties props;
private Logger logger;
private boolean supervised;
private String classpath;
private String classname;
private String argsString;
private String[] args;
private String serverName = "";
private String passwordFilePath = "";
private static final LocalStringsImpl strings = new LocalStringsImpl(RestartServer.class
);
///////////// static variables ///////////////////
private static final String magicProperty = "-DAS_RESTART=" + ProcessUtils.getPid();
private static final String[] normalProps = {magicProperty};
private static final int RESTART_NORMAL = 10;
private static final int RESTART_DEBUG_ON = 11;
private static final int RESTART_DEBUG_OFF = 12;
private static final String[] debuggerProps = {
magicProperty,
"-Xdebug",
"-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1323"};
}