com.redhat.rhn.taskomatic.core.BaseDaemon.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.rhn.taskomatic.core.BaseDaemon.java

Source

/**
 * Copyright (c) 2009--2014 Red Hat, Inc.
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * Red Hat trademarks are not licensed under GPLv2. No permission is
 * granted to use or replicate Red Hat trademarks that are incorporated
 * in this software or its documentation.
 */
package com.redhat.rhn.taskomatic.core;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.tanukisoftware.wrapper.WrapperListener;
import org.tanukisoftware.wrapper.WrapperManager;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * This is the base implementation for all RHN Java daemons.
 * It serves at the interface between the Tanuki service/daemon
 * wrapper library and also provides very basic lifecycle callbacks.
 * @version $Rev: $
 */
public abstract class BaseDaemon implements WrapperListener {

    public static final int LOG_DEBUG = WrapperManager.WRAPPER_LOG_LEVEL_DEBUG;
    public static final int LOG_INFO = WrapperManager.WRAPPER_LOG_LEVEL_INFO;
    public static final int LOG_ERROR = WrapperManager.WRAPPER_LOG_LEVEL_ERROR;
    public static final int LOG_FATAL = WrapperManager.WRAPPER_LOG_LEVEL_FATAL;

    public static final int SUCCESS = Integer.MIN_VALUE;

    /**
     * Interface method required by WrapperListener
     * @param argv Arguments configured in the daemon's config file
     * @return Integer indicating status (null indicates success, else value indicates
     * error code)
     */
    public Integer start(String[] argv) {
        Integer retval = null;
        Options options = buildOptionsList();
        int status = BaseDaemon.SUCCESS;
        if (options != null) {
            status = startupWithOptions(options, argv);
            if (status != BaseDaemon.SUCCESS) {
                retval = new Integer(status);
            }
        } else {
            status = startupWithoutOptions();
            if (status != BaseDaemon.SUCCESS) {
                retval = new Integer(status);
            }
        }
        return retval;
    }

    /**
     * Interface method required by WrapperListener
     * @param code int
     * @return int
     */
    public int stop(int code) {
        int retval = onShutdown(false);
        if (retval == BaseDaemon.SUCCESS) {
            return 0;
        }
        return retval;
    }

    /**
     * Interface method required by WrapperListener
     * @param event int
     */
    public void controlEvent(int event) {
        switch (event) {
        case WrapperManager.WRAPPER_CTRL_C_EVENT:
            onShutdown(true);
            break;
        case WrapperManager.WRAPPER_CTRL_CLOSE_EVENT:
        case WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT:
        case WrapperManager.WRAPPER_CTRL_SHUTDOWN_EVENT:
            onShutdown(false);
            break;
        default:
            break;
        }
    }

    /**
     * Convenience method to allow daemon implementations to log messages directly
     * to the host system's system log, ie not log4j or commons-logging. If a
     * java.lang.Throwable is passed in then the stack trace will be logged as well.
     * @param logLevel Desired log level
     * @param msg Message to log
     * @param err Optional
     */
    public void logMessage(int logLevel, String msg, Throwable err) {
        StringBuilder buf = new StringBuilder();
        buf.append(msg);
        if (err != null) {
            buf.append("\n");
            StringWriter writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            err.printStackTrace(printWriter);
            printWriter.flush();
            buf.append(writer.toString());
        }
        WrapperManager.log(logLevel, buf.toString());
    }

    /**
     * Registers the daemon implementation with the Tanuki wrapper
     * @param argv startup parameters (if any)
     */
    protected void registerImplementation(String[] argv) {
        WrapperManager.start(this, argv);
    }

    /**
     * Creates the "master" list of options which the daemon implementation
     * knows about. Implementations should override this method if they need
     * param parsing.
     * @return org.apache.commons.cli.Options instance populated with options or null if
     * no options
     */
    protected Options buildOptionsList() {
        return null;
    }

    /**
     * Lifecycle method called when startup parameters cannot be parsed. This gives
     * the daemon implementation an opportunity to do something about the error such
     * as display a usage message.
     * @param e
     * @return int indicates error code. If BaseDaemon.SUCCESS is returned, then
     * the framework will still try to start the daemon implementation _without_ parameters.
     */
    protected int onOptionsParseError(ParseException e) {
        return BaseDaemon.SUCCESS;
    }

    /**
     * Lifecycle method called when the daemon implementation is started. This
     * method _must_ return otherwise the daemon will appear to hang at startup.
     * This implies that all "real" work needs to be done in a separate thread.
     * @param commandLine Parsed params, if present. Otherwise null.
     * @return int indicates status where <code>BaseDaemon.SUCCESS</code>
     * indicates success and any other number indicates failure
     */
    protected abstract int onStartup(CommandLine commandLine);

    /**
     * Lifecycle method called when the daemon implementation is stopped.
     * @param breakFromUser True if the user sent a Ctrl-C
     * @return int indicates status where <code>BaseDaemon.SUCCESS</code>
     * indicates success and any other number indicates an error
     */
    protected abstract int onShutdown(boolean breakFromUser);

    /**
     * Parse startup options using jakarta-commons-cli and start
     * the daemon implementation
     * @param options Master list of options built by the daemon implementation
     * @param argv Startup arguments
     * @return int indicates status where <code>BaseDaemon.SUCCESS</code>
     * indicates success and any other number indicates failure
     *
     * @see buildOptionsList
     */
    private int startupWithOptions(Options options, String[] argv) {
        int retval = BaseDaemon.SUCCESS;
        CommandLineParser parser = null;
        try {
            parser = new PosixParser();
            CommandLine cl = parser.parse(options, argv);
            retval = onStartup(cl);
        } catch (ParseException e) {
            retval = onOptionsParseError(e);
            if (retval == BaseDaemon.SUCCESS) {
                retval = onStartup(null);
            }
        }
        return retval;
    }

    /**
     * Start the daemon implementation with no startup parameters
     * @return int indicates status where <code>BaseDaemon.SUCCESS</code>
     * indicates success and any other number indicates failure
     */
    private int startupWithoutOptions() {
        return onStartup(null);
    }
}