All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.enterprise.v3.admin.RestartServer Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * 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"};
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy