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

org.rhq.enterprise.communications.util.prefs.Setup Maven / Gradle / Ivy

The newest version!
/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.communications.util.prefs;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.prefs.Preferences;
import mazz.i18n.Logger;
import mazz.i18n.Msg;
import org.rhq.enterprise.communications.i18n.CommI18NFactory;
import org.rhq.enterprise.communications.i18n.CommI18NResourceKeys;

/**
 * This provides a way to initially setup a set of preferences. Normally, this is used when a new installation is laid
 * down and the user needs to be asked for some preference values to customize the installation with the user's own
 * settings. This could concievably also be used to customize any set of preferences, even those that already exist or
 * those that have recently been {@link PreferencesUpgrade upgraded}.
 *
 * @author John Mazzitelli
 */
public class Setup {
    /**
     * If the user enters this string at a prompt, it means the preference should not be set and its internal default
     * should be assumed.
     */
    private static final String INTERNAL_DEFAULT_PROMPT_VALUE = "!*";

    /**
     * If the user enters this string at a prompt, it means the preference help text needs to be shown to the user.
     */
    private static final String HELP_PROMPT_VALUE = "!?";

    /**
     * If the user enters this string at a prompt, it means the user wants to prematurely stop the setup, but leave the
     * settings already setup intact.
     */
    private static final String STOP_PROMPT_VALUE = "!+";

    /**
     * If the user enters this string at a prompt, it means the user wants to prematurely stop the setup, and abort any
     * changes made, thus reverting the preferences back to their original values.
     */
    private static final String CANCEL_PROMPT_VALUE = "!-";

    /**
     * Logger
     */
    private static final Logger LOG = CommI18NFactory.getLogger(Setup.class);

    /**
     * I18N messages for output to console
     */
    private static final Msg MSG = CommI18NFactory.getMsg();

    private final Preferences m_preferences;
    private final String m_introMessage;
    private final List m_instructions;
    private final PromptInput m_in;
    private final PrintWriter m_out;

    /**
     * Creates a new {@link Setup} object.
     *
     * @param preferences   the set of preferences that need to be customized
     * @param intro_message a human-readable string that will be {@link #getOut() output} prior to starting the
     *                      {@link #setup()} - this can be used to explain to the user what these set of preferences are
     *                      for (may be null)
     * @param instructions  the set of instructions this class will use to customize the preferences
     * @param in            the input where the answers to the setup prompts will come from
     * @param out           the output stream where the setup prompts will be output
     */
    public Setup(Preferences preferences, String intro_message, List instructions, PromptInput in,
        PrintWriter out) {
        m_preferences = preferences;
        m_introMessage = intro_message;
        m_instructions = instructions;
        m_in = in;
        m_out = out;
    }

    /**
     * Returns the preferences that are to be customized via this setup instance.
     *
     * @return preferences
     */
    public Preferences getPreferences() {
        return m_preferences;
    }

    /**
     * Returns the intro message that will be printed to the {@link #getOut() output stream} before the {@link #setup()}
     * begins processing the instructions. If this is null, it will be ignored.
     *
     * @return human-readable introductory message (may be null)
     */
    public String getIntroMessage() {
        return m_introMessage;
    }

    /**
     * Returns the set of instructions that are used to ask the setup questions.
     *
     * @return instructions
     */
    public List getInstructions() {
        return m_instructions;
    }

    /**
     * Returns the input object where the answers to the setup prompts will be read from.
     *
     * @return in
     */
    public PromptInput getIn() {
        return m_in;
    }

    /**
     * Returns the output stream where the prompts and any other output messages will be written to.
     *
     * @return out
     */
    public PrintWriter getOut() {
        return m_out;
    }

    /**
     * Performs the setup by asking questions and setting preferences according to this object's
     * {@link #getInstructions() setup instructions}.
     *
     * @return true if the setup finished; false if the user canceled the setup and reverted
     *         back to the original values
     *
     * @throws RuntimeException if failed to access the preferences backend store
     */
    public boolean setup() {
        PromptInput in = getIn();
        PrintWriter out = getOut();
        List all_instructions = getInstructions();
        Preferences preferences = getPreferences();
        ByteArrayOutputStream backup = backupPreferences(preferences);

        // print an introductory message to the user explaining that we are going to ask some questions
        if (all_instructions.size() > 0) {
            if (getIntroMessage() != null) {
                out.println(getIntroMessage());
            }

            out.println(MSG.getMsg(CommI18NResourceKeys.SETUP_STANDARD_INTRO));
        }

        boolean user_stop = false; // will be true if the user asked to prematurely stop the setup but keep the current values
        boolean user_cancel = false; // will be true if the user asked to cancel the setup and restore the original values

        // for each setup instruction, ask the user to define the preference value as per the instruction
        for (SetupInstruction instruction : all_instructions) {
            // give the instruction the set of preferences that are being setup and
            // give the instruction a chance to pre-process its internal state before we begin
            LOG.debug(CommI18NResourceKeys.SETUP_PREPROCESS_INSTRUCTION, instruction);
            instruction.setPreferences(preferences);
            instruction.preProcess();

            // get the information needed to prompt the user for the new preference value
            String pref_name = instruction.getPreferenceName();
            String default_value = instruction.getDefaultValue();
            String prompt_message = instruction.getPromptMessage();
            String help_message = instruction.getHelpMessage();
            boolean no_echo = instruction.isUsingNoEchoPrompt();
            String new_value = null;

            if (prompt_message != null) {
                boolean is_valid;
                do {
                    new_value = prompt(prompt_message, pref_name, help_message, default_value, in, out, no_echo);

                    is_valid = true; // assume this new value is valid unless our validity checker (if one exists) tells us otherwise

                    if (new_value != null) {
                        if (new_value.length() > 0) {
                            user_stop = new_value.equals(STOP_PROMPT_VALUE);
                            user_cancel = new_value.equals(CANCEL_PROMPT_VALUE);

                            if ((instruction.getValidityChecker() != null) && !(user_stop || user_cancel)) {
                                is_valid = instruction.getValidityChecker().checkValidity(pref_name, new_value,
                                    preferences, out);
                            }
                        } else {
                            new_value = default_value;
                        }
                    }
                } while (!is_valid);

                if (!(user_stop || user_cancel)) {
                    LOG.debug(CommI18NResourceKeys.SETUP_NEW_VALUE, pref_name, new_value);
                }
            } else {
                // not required to prompt - use the instruction's default - no need to validate since that value came from the instruction directly
                new_value = default_value;
                LOG.debug(CommI18NResourceKeys.SETUP_NEW_VALUE_NO_PROMPT, pref_name, new_value);
            }

            if (user_stop || user_cancel) {
                break; // user asked to stop so do not process any more instructions
            }

            // now that we got the new value, put it in the preferences
            if (new_value != null) {
                preferences.put(pref_name, new_value);
            } else {
                // a null means the user wants to pick up our internal defaults, so we'll just remove the preference
                preferences.remove(pref_name);
            }

            // give the instruction a chance to post-process the preferences in case it needs to
            // do some more things to the preferences after the user entered the new preference value
            LOG.debug(CommI18NResourceKeys.SETUP_POSTPROCESS_INSTRUCTION, instruction);
            instruction.postProcess();
        }

        // print out a message so the user knows we are done asking questions and log the new set of preference values
        if (user_stop) {
            out.println(MSG.getMsg(CommI18NResourceKeys.SETUP_USER_STOP, preferences.absolutePath()));
        } else if (user_cancel) {
            out.println(MSG.getMsg(CommI18NResourceKeys.SETUP_USER_CANCEL, preferences.absolutePath()));
            restorePreferences(backup);
        } else {
            out.println(MSG.getMsg(CommI18NResourceKeys.SETUP_COMPLETE, preferences.absolutePath()));
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(CommI18NResourceKeys.SETUP_COMPLETE_WITH_DUMP, preferencesDump(preferences));
        }

        return !user_cancel;
    }

    /**
     * Prints the prompt message to the output stream and waits for the new value to be input. If the input is empty
     * (e.g. the user just hit the return key), an empty string is returned. A help message will be shown if the user
     * asks for it.
     *
     * 

Note that the default_value is only used to show in the prompt. Specifically, it will not be * returned if the user simply hit the ENTER key without entering a value. In that case, an empty string is * returned; the caller should detect this and set the value to the default as appropriate.

* *

The returned value will be null if the user wants to rely on the system internal default and not * set the preference value at all.

* * @param prompt_message the prompt message printed to the output stream that tells the user what is being asked * for * @param pref_name the actual preference name that is to be set to the answer of the prompt * @param help_message a more detailed help message that tells the user what is being asked for * @param default_value what the value should be if the user just hits the ENTER key * @param in where the user input is coming from * @param out the stream where the prompt messages will be written * @param no_echo if true, user should not see what is being typed at the prompt * * @return the new value entered by the user that came across in the input stream (may be null) * * @throws RuntimeException if failed to read from the input stream */ protected String prompt(String prompt_message, String pref_name, String help_message, String default_value, PromptInput in, PrintWriter out, boolean no_echo) { String full_prompt = prompt_message + " [" + ((default_value != null) ? default_value : INTERNAL_DEFAULT_PROMPT_VALUE) + "] : "; String new_value = ""; boolean keep_asking = true; while (keep_asking) { out.print(full_prompt); out.flush(); try { if (no_echo) { new_value = in.readLineNoEcho(); } else { new_value = in.readLine(); } } catch (IOException e) { // this will abort the entire setup - but there is nothing we can do about this error, so that's what we have to do throw new RuntimeException(e); } if (new_value.equals(HELP_PROMPT_VALUE)) { out.println(help_message); out.println("(" + pref_name + ")"); } else if (new_value.equals(INTERNAL_DEFAULT_PROMPT_VALUE)) { new_value = null; keep_asking = false; } else { keep_asking = false; } } return new_value; } /** * Given a set of preferences, this will dump each name/value in the returned string separated with a newline. * * @param prefs the preferences whose values are to be dumped in the given string * * @return the preference name/value pairs separated by newlines */ protected String preferencesDump(Preferences prefs) { try { StringBuffer dump = new StringBuffer(prefs.toString() + '\n'); String[] pref_keys = prefs.keys(); for (int i = 0; i < pref_keys.length; i++) { dump.append(pref_keys[i]); dump.append('='); dump.append(prefs.get(pref_keys[i], "<>")); dump.append('\n'); } return dump.toString(); } catch (Exception e) { return e.toString(); } } /** * Given a set of preferences, this will export them and return them in the given stream; in effect backing up their * values. * * @param preferences the preferences to backup * * @return the backed up preferences. * * @throws RuntimeException if failed to access the backing store */ private ByteArrayOutputStream backupPreferences(Preferences preferences) throws RuntimeException { try { ByteArrayOutputStream backup = new ByteArrayOutputStream(); preferences.exportSubtree(backup); return backup; } catch (Exception e) { // should rarely occur, but if it does, something is probably wrong with the backing store so no need to continue throw new RuntimeException(e); } } /** * Given a stream containing a {@link #backupPreferences(Preferences) backed up set of preferences}, this will * import them back in; in effect restoring their values. * * @param backup the original, backed up preferences * * @throws RuntimeException if failed to access the backing store */ private void restorePreferences(ByteArrayOutputStream backup) throws RuntimeException { try { getPreferences().clear(); Preferences.importPreferences(new ByteArrayInputStream(backup.toByteArray())); } catch (Exception e) { // should rarely occur, but if it does, something is probably wrong with the backing store so no need to continue throw new RuntimeException(e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy