org.n0pe.asadmin.AsAdmin.java Source code

Java tutorial

Introduction

Here is the source code for org.n0pe.asadmin.AsAdmin.java

Source

/*
 * Copyright (c) 2010, Paul Merlin. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.n0pe.asadmin;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;

import org.n0pe.asadmin.commands.Database;
import org.n0pe.asadmin.commands.Domain;

/**
 * asadmin command execution facility built as a multipleton which discriminator is a configuration provider.
 * 
 * TODO : allows AsAdminCommands to provide input lines for stdin, implements this command :
 *        echo y | asadmin generate-diagnostic-report --outputfile report-dosadi.jar domainName
 * 
 * TODO : handle asadmin invocation return codes with exceptions
 * 
 * @author Paul Merlin <eskatos@n0pe.org>
 * @author Christophe Souvignier <chris.so@free.fr>
 */
public class AsAdmin {

    private static final String ASADMIN_FAILED = "failed";
    private static final String OUTPUT_PREFIX = "[ASADMIN] ";
    private static Map<IAsAdminConfig, AsAdmin> instances;
    public static final String HOST_OPT = "--host";
    public static final String PORT_OPT = "--port";
    public static final String SECURE_OPT = "--secure";
    public static final String USER_OPT = "--user";
    public static final String PASSWORDFILE_OPT = "--passwordfile";
    public static String ASADMIN_COMMAND_NAME;

    static {
        ASADMIN_COMMAND_NAME = SystemUtils.IS_OS_WINDOWS ? "asadmin.bat" : "asadmin";
    }

    private IAsAdminConfig config;

    /**
     * Get a asadmin instance configured with the given configuration provider.
     * 
     * @param config
     * @return
     */
    public static AsAdmin getInstance(final IAsAdminConfig config) {
        if (instances == null) {
            instances = new HashMap<IAsAdminConfig, AsAdmin>(1);
        }
        AsAdmin instance = instances.get(config);
        if (instance == null) {
            instance = new AsAdmin(config);
            instances.put(config, instance);
        }
        return instance;
    }

    private AsAdmin(final IAsAdminConfig config) {
        this.config = config;
    }

    /**
     * Run the given list of AsAdmin command.
     * 
     * @param cmdList AsAdmin commands to be run
     * @throws org.n0pe.asadmin.AsAdminException AsAdminException
     */
    public void run(final AsAdminCmdList cmdList) throws AsAdminException {
        final Iterator<IAsAdminCmd> it = cmdList.iterator();
        while (it.hasNext()) {
            run(it.next());
        }
    }

    /**
     * Run the given AsAdmin command.
     * 
     * @param cmd AsAdmin command to be run
     * @throws org.n0pe.asadmin.AsAdminException AsAdminException
     */
    public void run(final IAsAdminCmd cmd) throws AsAdminException {
        try {
            final File gfBinPath = new File(config.getGlassfishHome() + File.separator + "bin");
            final String[] cmds = buildProcessParams(cmd, config);
            cmds[0] = gfBinPath + File.separator + cmds[0];
            int exitCode;
            final Process proc;
            String[] env = buildEnvironmentStrings(config.getEnvironmentVariables());
            if (SystemUtils.IS_OS_WINDOWS) {
                // Windows
                final String command = "\"\"" + StringUtils.join(cmds, "\" \"") + "\"\"";
                final String[] windowsCommand;
                if (SystemUtils.IS_OS_WINDOWS_95 || SystemUtils.IS_OS_WINDOWS_98 || SystemUtils.IS_OS_WINDOWS_ME) {
                    windowsCommand = new String[] { "command.com", "/C", command };
                } else {
                    windowsCommand = new String[] { "cmd.exe", "/C", command };
                }
                outPrintln("Will run the following command: " + StringUtils.join(windowsCommand, " "));
                if (env.length > 0) {
                    proc = Runtime.getRuntime().exec(windowsCommand, env);
                } else {
                    proc = Runtime.getRuntime().exec(windowsCommand);
                }
            } else {
                // Non Windows
                outPrintln("Will run the following command: " + StringUtils.join(cmds, " "));
                proc = Runtime.getRuntime().exec(cmds, env);
            }
            final ProcessStreamGobbler errorGobbler = new ProcessStreamGobbler(cmd, proc.getErrorStream(),
                    ProcessStreamGobbler.ERROR);
            final ProcessStreamGobbler outputGobbler = new ProcessStreamGobbler(cmd, proc.getInputStream(),
                    ProcessStreamGobbler.OUTPUT);
            errorGobbler.start();
            outputGobbler.start();
            exitCode = proc.waitFor();
            if (exitCode != 0) {
                throw new AsAdminException("asadmin invocation failed and returned : " + String.valueOf(exitCode));
            }
        } catch (final InterruptedException ex) {
            throw new AsAdminException("AsAdmin error occurred: " + ex.getMessage(), ex);
        } catch (final IOException ex) {
            throw new AsAdminException("AsAdmin error occurred: " + ex.getMessage(), ex);
        }
    }

    public static String[] buildProcessParams(final IAsAdminCmd cmd, final IAsAdminConfig config)
            throws AsAdminException {
        final List<String> pbParams = new ArrayList<String>();
        pbParams.add(ASADMIN_COMMAND_NAME);
        pbParams.add(cmd.getActionCommand());
        if (!StringUtils.isEmpty(config.getHost()) && !Domain.START.equals(cmd.getActionCommand())
                && !Domain.STOP.equals(cmd.getActionCommand()) && !Database.STOP.equals(cmd.getActionCommand())
                && !Database.START.equals(cmd.getActionCommand())) {
            pbParams.add(HOST_OPT);
            pbParams.add(config.getHost());
        }
        if (!StringUtils.isEmpty(config.getPort()) && !Domain.START.equals(cmd.getActionCommand())
                && !Domain.STOP.equals(cmd.getActionCommand()) && !Database.STOP.equals(cmd.getActionCommand())
                && !Database.START.equals(cmd.getActionCommand())) {
            pbParams.add(PORT_OPT);
            pbParams.add(config.getPort());
        }
        if (config.isSecure()) {
            pbParams.add(SECURE_OPT);
        }
        if (cmd.needCredentials()) {
            pbParams.add(USER_OPT);
            pbParams.add(config.getUser());
            pbParams.add(PASSWORDFILE_OPT);
            pbParams.add(cmd.handlePasswordFile(config.getPasswordFile()));

        }
        pbParams.addAll(Arrays.asList(cmd.getParameters()));
        return pbParams.toArray(new String[pbParams.size()]);
    }

    private static void outPrintln(final String message) {
        System.out.print(OUTPUT_PREFIX);
        System.out.println(message);
    }

    private static void errPrintln(final String message) {
        System.out.print(OUTPUT_PREFIX);
        System.out.println(message);
    }

    /**
     * Ensure variable names do not contains spaces and quote their values if needed.
     */
    /* package */ static String[] buildEnvironmentStrings(Map<String, String> envVariables) {
        if (envVariables == null || envVariables.isEmpty()) {
            return new String[] {};
        }
        String[] array = new String[envVariables.size()];
        int idx = 0;
        for (Map.Entry<String, String> eachEntry : envVariables.entrySet()) {
            String key = eachEntry.getKey().trim();
            if (!key.matches("^\\S+$")) {
                throw new IllegalArgumentException("Environment variable names cannot contain spaces: " + key);
            }
            String value = eachEntry.getValue();
            array[idx] = key + "=" + quoteArgument(value);
            idx++;
        }
        return array;
    }

    private static final String SINGLE_QUOTE = "\'";
    private static final String DOUBLE_QUOTE = "\"";

    private static String quoteArgument(final String argument) {
        String cleanedArgument = argument.trim();
        // strip the quotes from both ends
        while (cleanedArgument.startsWith(SINGLE_QUOTE) || cleanedArgument.startsWith(DOUBLE_QUOTE)) {
            cleanedArgument = cleanedArgument.substring(1);
        }
        while (cleanedArgument.endsWith(SINGLE_QUOTE) || cleanedArgument.endsWith(DOUBLE_QUOTE)) {
            cleanedArgument = cleanedArgument.substring(0, cleanedArgument.length() - 1);
        }
        final StringBuffer sb = new StringBuffer();
        if (cleanedArgument.indexOf(DOUBLE_QUOTE) > -1) {
            if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1) {
                throw new IllegalArgumentException("Can't handle single and double quotes in same argument");
            } else {
                return sb.append(SINGLE_QUOTE).append(cleanedArgument).append(SINGLE_QUOTE).toString();
            }
        } else if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1 || cleanedArgument.indexOf(" ") > -1) {
            return sb.append(DOUBLE_QUOTE).append(cleanedArgument).append(DOUBLE_QUOTE).toString();
        } else {
            return cleanedArgument;
        }
    }

    /**
     * TODO : take a logger as constructor parameter and remove type
     */
    private static class ProcessStreamGobbler extends Thread {

        private static final int OUTPUT = 0;
        private static final int ERROR = 1;
        private AbstractAsAdminCmd cmd;
        private InputStream is;
        private int type = OUTPUT;

        private ProcessStreamGobbler(final IAsAdminCmd cmd, InputStream is, int type) {
            this.is = is;
            this.type = type;
            if (AbstractAsAdminCmd.class.isAssignableFrom(cmd.getClass())) {
                this.cmd = (AbstractAsAdminCmd) cmd;
            }
        }

        @Override
        @SuppressWarnings("CallToThreadDumpStack")
        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line = null;
                while ((line = br.readLine()) != null) {

                    switch (type) {
                    case OUTPUT:
                        if (cmd != null) {
                            cmd.appendStandardOutputLine(line);
                        }
                        outPrintln("[OUTPUT] " + line);
                        break;
                    case ERROR:
                        if (cmd != null) {
                            cmd.appendErrorOutputLine(line);
                        }
                        errPrintln("[ERROR]  " + line);
                    }
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }

    }

}