org.opendatakit.appengine.updater.UpdaterWindow.java Source code

Java tutorial

Introduction

Here is the source code for org.opendatakit.appengine.updater.UpdaterWindow.java

Source

/*
 * Copyright (C) 2016 University of Washington.
 *
 * 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.opendatakit.appengine.updater;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.util.Formatter;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;
import org.opendatakit.apache.commons.exec.ExecuteException;
import org.opendatakit.apache.commons.exec.ExecuteResultHandler;
import org.opendatakit.apache.commons.exec.ExecuteStreamHandler;
import org.opendatakit.apache.commons.exec.StreamPumperBuilder.StreamType;

public class UpdaterWindow implements WindowListener {

    private static final int HorizontalSpacing = 20;
    private static File myJarDir;
    private static ResourceBundle translations;
    private CommandLine cmd;
    private JFrame frame;
    private JTextField txtEmail;
    private JTextField txtToken;
    private JLabel lblWarning;
    private JTextPane editorArea;
    private JScrollPane editorScrollPane;
    private JButton btnDeleteToken;
    private JButton btnChoose;
    private JButton btnUpload;
    private JButton btnRollback;
    private StateExecuteResultHandler activeHandler;

    /**
     * Create the application.
     */
    public UpdaterWindow(CommandLine cmd) {
        super();
        AnnotationProcessor.process(this);// if not using AOP
        this.cmd = cmd;
        frame = new JFrame();
        frame.setBounds(100, 100, isLinux() ? 720 : 680, 595);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.addWindowListener(new WindowListener() {
            @Override
            public void windowOpened(WindowEvent e) {
            }

            @Override
            public void windowClosing(WindowEvent e) {
            }

            @Override
            public void windowClosed(WindowEvent e) {
            }

            @Override
            public void windowIconified(WindowEvent e) {
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
            }

            @Override
            public void windowActivated(WindowEvent e) {
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
            }
        });

        JLabel lblEmail = new JLabel(t(TranslatedStrings.EMAIL_LABEL));

        txtEmail = new JTextField();
        txtEmail.setFocusable(true);
        txtEmail.setEditable(true);
        txtEmail.setColumns(60);
        txtEmail.setMaximumSize(txtEmail.getPreferredSize());
        if (cmd.hasOption(ArgumentNameConstants.EMAIL)) {
            txtEmail.setText(cmd.getOptionValue(ArgumentNameConstants.EMAIL));
        }
        lblEmail.setLabelFor(txtEmail);

        JLabel lblToken = new JLabel(t(TranslatedStrings.TOKEN_GRANTING_LABEL));

        txtToken = new JTextField();
        txtToken.setColumns(60);
        txtToken.setMaximumSize(txtToken.getPreferredSize());
        txtToken.setFocusable(false);
        txtToken.setEditable(false);
        if (cmd.hasOption(ArgumentNameConstants.TOKEN_GRANTING_CODE)) {
            txtToken.setText(cmd.getOptionValue(ArgumentNameConstants.TOKEN_GRANTING_CODE));
        }
        lblToken.setLabelFor(txtToken);

        // set up listener for updating warning message
        txtEmail.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                updateUI();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                updateUI();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                updateUI();
            }

        });

        // set up listener for updating warning message
        txtToken.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                updateUI();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                updateUI();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                updateUI();
            }

        });

        if ((txtEmail.getText().length() > 0) && ((txtToken.getText().length() > 0) || perhapsHasToken())) {
            lblWarning = new JLabel(t(TranslatedStrings.WARNING_ERRANT_LABEL));
        } else {
            lblWarning = new JLabel(t(TranslatedStrings.WARNING_REDIRECT_LABEL));
        }

        JLabel outputArea = new JLabel(t(TranslatedStrings.OUTPUT_LBL));
        editorArea = new JTextPane(new DefaultStyledDocument());
        editorArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
        //Put the editor pane in a scroll pane.
        editorScrollPane = new JScrollPane(editorArea);
        editorScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        editorScrollPane.setPreferredSize(new Dimension(400, 300));
        editorScrollPane.setMinimumSize(new Dimension(10, 10));

        outputArea.setLabelFor(editorScrollPane);
        // Create a container so that we can add a title around
        // the scroll pane. Can't add a title directly to the
        // scroll pane because its background would be white.
        // Lay out the label and scroll pane from top to bottom.
        JPanel listPane = new JPanel();
        listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
        listPane.add(outputArea);
        listPane.add(Box.createRigidArea(new Dimension(0, 5)));
        listPane.add(editorScrollPane);
        listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        btnDeleteToken = new JButton(t(TranslatedStrings.DELETE_TOKEN_LABEL));
        btnDeleteToken.addActionListener(new DeleteTokenActionListener());
        btnDeleteToken.setEnabled(perhapsHasToken());

        btnChoose = new JButton(t(TranslatedStrings.GET_TOKEN_LABEL));
        if ((txtEmail.getText().length() > 0) && (txtToken.getText().length() > 0) || perhapsHasToken()) {
            if (perhapsHasToken()) {
                btnChoose.setText(t(TranslatedStrings.VERIFY_TOKEN_LABEL));
            } else {
                btnChoose.setText(t(TranslatedStrings.SET_TOKEN_LABEL));
            }
        } else {
            btnChoose.setText(t(TranslatedStrings.GET_TOKEN_LABEL));
        }
        btnChoose.addActionListener(new GetTokenActionListener());
        btnChoose.setEnabled(txtEmail.getText().length() > 0);

        btnUpload = new JButton(t(TranslatedStrings.UPLOAD_LABEL));
        btnUpload.addActionListener(new UploadActionListener());
        btnUpload.setEnabled((txtEmail.getText().length() > 0) && perhapsHasToken());

        btnRollback = new JButton(t(TranslatedStrings.ROLLBACK_LABEL));
        btnRollback.addActionListener(new RollbackActionListener());
        btnRollback.setEnabled((txtEmail.getText().length() > 0) && perhapsHasToken());

        GroupLayout groupLayout = new GroupLayout(frame.getContentPane());
        groupLayout
                .setHorizontalGroup(
                        groupLayout.createSequentialGroup().addContainerGap()
                                .addGroup(groupLayout.createParallelGroup(Alignment.LEADING).addComponent(lblEmail)
                                        .addComponent(txtEmail).addComponent(lblToken).addComponent(txtToken)
                                        .addComponent(lblWarning).addComponent(listPane)
                                        .addGroup(groupLayout.createSequentialGroup().addComponent(btnDeleteToken)
                                                .addGap(3 * HorizontalSpacing).addComponent(btnChoose)
                                                .addGap(HorizontalSpacing).addComponent(btnUpload)
                                                .addGap(3 * HorizontalSpacing, 4 * HorizontalSpacing,
                                                        Short.MAX_VALUE)
                                                .addComponent(btnRollback)))
                                .addContainerGap());

        groupLayout.setVerticalGroup(groupLayout.createSequentialGroup().addContainerGap().addComponent(lblEmail)
                .addPreferredGap(ComponentPlacement.RELATED).addComponent(txtEmail)
                .addPreferredGap(ComponentPlacement.UNRELATED).addComponent(lblToken)
                .addPreferredGap(ComponentPlacement.RELATED).addComponent(txtToken)
                .addPreferredGap(ComponentPlacement.UNRELATED).addComponent(lblWarning)
                .addPreferredGap(ComponentPlacement.UNRELATED).addComponent(listPane)
                .addPreferredGap(ComponentPlacement.UNRELATED)
                .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE).addComponent(btnDeleteToken)
                        .addComponent(btnChoose).addComponent(btnUpload).addComponent(btnRollback))
                .addContainerGap());

        frame.getContentPane().setLayout(groupLayout);

        frame.addWindowListener(this);
    }

    public static File getJarDir() {
        return myJarDir;
    }

    public static String t(String id) {
        return translations.getString(id);
    }

    public static String fmt(String id, Object... args) {
        StringBuilder sb = new StringBuilder();
        Formatter formatter = null;
        try {
            formatter = new Formatter(sb, Locale.getDefault());
            formatter.format(t(id), args);
            return formatter.toString();
        } finally {
            if (formatter != null) {
                formatter.close();
            }
        }
    }

    /**
     * Launch the application.
     */
    public static void main(String[] args) {

        translations = ResourceBundle.getBundle(TranslatedStrings.class.getCanonicalName(), Locale.getDefault());

        Options options = addOptions();

        // get location of this jar
        String reflectedJarPath = UpdaterWindow.class.getProtectionDomain().getCodeSource().getLocation().getFile();
        // remove %20 substitutions.
        reflectedJarPath = reflectedJarPath.replace("%20", " ");
        File myJar = new File(reflectedJarPath, Preferences.JAR_NAME);
        System.out.println("CodeSource Location: " + myJar.getAbsolutePath());
        File cleanJarPath = null;
        if (myJar.exists()) {
            try {
                cleanJarPath = myJar.getCanonicalFile();
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        if (cleanJarPath == null) {
            // try finding this within our working directory
            String dir = System.getProperty("user.dir");
            if (dir != null) {
                File myWD = new File(dir);
                if (myWD.exists()) {
                    myJar = new File(myWD, Preferences.JAR_NAME);
                    System.out.println("user.dir path: " + myJar.getAbsolutePath());
                    if (myJar.exists()) {
                        try {
                            cleanJarPath = myJar.getCanonicalFile();
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    }
                }
            }
        }

        if (cleanJarPath != null) {
            myJarDir = cleanJarPath.getParentFile();
            System.out.println(fmt(TranslatedStrings.DIR_RUNNABLE_JAR, myJarDir.getAbsolutePath()));
        } else {
            myJarDir = null;
        }

        CommandLineParser parser = new DefaultParser();
        final CommandLine cmdArgs;

        try {
            cmdArgs = parser.parse(options, args);
        } catch (ParseException e1) {
            System.out.println(fmt(TranslatedStrings.LAUNCH_FAILED, e1.getMessage()));
            showHelp(options);
            System.exit(1);
            return;
        }

        if (cmdArgs.hasOption(ArgumentNameConstants.HELP)) {
            showHelp(options);
            System.exit(0);
            return;
        }

        if (cmdArgs.hasOption(ArgumentNameConstants.VERSION)) {
            showVersion();
            System.exit(0);
            return;
        }

        if (myJarDir == null && !cmdArgs.hasOption(ArgumentNameConstants.INSTALL_ROOT)) {
            System.out.println(fmt(TranslatedStrings.INSTALL_ROOT_REQUIRED, Preferences.JAR_NAME,
                    ArgumentNameConstants.INSTALL_ROOT));
            showHelp(options);
            System.exit(1);
            return;
        }

        // required for all operations
        if (cmdArgs.hasOption(ArgumentNameConstants.NO_UI) && !cmdArgs.hasOption(ArgumentNameConstants.EMAIL)) {
            System.out.println(fmt(TranslatedStrings.ARG_IS_REQUIRED, ArgumentNameConstants.EMAIL));
            showHelp(options);
            System.exit(1);
            return;
        }

        // update appEngine with the local configuration
        if (cmdArgs.hasOption(ArgumentNameConstants.UPLOAD)) {

            if (cmdArgs.hasOption(ArgumentNameConstants.CLEAR)) {
                System.out.println(fmt(TranslatedStrings.CONFLICTING_ARGS_CMD, ArgumentNameConstants.UPLOAD,
                        ArgumentNameConstants.CLEAR));
            }

            if (cmdArgs.hasOption(ArgumentNameConstants.ROLLBACK)) {
                System.out.println(fmt(TranslatedStrings.CONFLICTING_ARGS_CMD, ArgumentNameConstants.UPLOAD,
                        ArgumentNameConstants.ROLLBACK));
            }

            if (!cmdArgs.hasOption(ArgumentNameConstants.EMAIL)) {
                System.out.println(fmt(TranslatedStrings.ARG_IS_REQUIRED_CMD, ArgumentNameConstants.EMAIL));
            }
            showHelp(options);
            System.exit(1);
            return;
        }

        // rollback any stuck outstanding configuration transaction on appEngine infrastructure
        if (cmdArgs.hasOption(ArgumentNameConstants.ROLLBACK)) {

            if (cmdArgs.hasOption(ArgumentNameConstants.CLEAR)) {
                System.out.println(fmt(TranslatedStrings.CONFLICTING_ARGS_CMD, ArgumentNameConstants.ROLLBACK,
                        ArgumentNameConstants.CLEAR));
            }

            if (cmdArgs.hasOption(ArgumentNameConstants.UPLOAD)) {
                System.out.println(fmt(TranslatedStrings.CONFLICTING_ARGS_CMD, ArgumentNameConstants.ROLLBACK,
                        ArgumentNameConstants.UPLOAD));
            }

            if (!cmdArgs.hasOption(ArgumentNameConstants.EMAIL)) {
                System.out.println(fmt(TranslatedStrings.ARG_IS_REQUIRED_CMD, ArgumentNameConstants.EMAIL));
            }
            showHelp(options);
            System.exit(1);
            return;
        }

        if (cmdArgs.hasOption(ArgumentNameConstants.CLEAR)) {

            if (cmdArgs.hasOption(ArgumentNameConstants.ROLLBACK)) {
                System.out.println(fmt(TranslatedStrings.CONFLICTING_ARGS_CMD, ArgumentNameConstants.CLEAR,
                        ArgumentNameConstants.ROLLBACK));
            }

            if (cmdArgs.hasOption(ArgumentNameConstants.UPLOAD)) {
                System.out.println(fmt(TranslatedStrings.CONFLICTING_ARGS_CMD, ArgumentNameConstants.CLEAR,
                        ArgumentNameConstants.UPLOAD));
            }
            showHelp(options);
            System.exit(1);
            return;
        }

        if (!cmdArgs.hasOption(ArgumentNameConstants.NO_UI)) {

            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    try {
                        // Set System L&F
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

                        UpdaterWindow window = new UpdaterWindow(cmdArgs);
                        window.frame.setTitle(fmt(TranslatedStrings.AGG_INSTALLER_VERSION, Preferences.VERSION));
                        ImageIcon icon = new ImageIcon(
                                UpdaterWindow.class.getClassLoader().getResource("odkupdater.png"));
                        window.frame.setIconImage(icon.getImage());
                        window.frame.setVisible(true);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } else {

            try {

                UpdaterCLI aggregateInstallerCLI = new UpdaterCLI(cmdArgs);
                aggregateInstallerCLI.run();

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static boolean isLinux() {
        String os = System.getProperty("os.name").toLowerCase();
        return (os.contains("nix") || os.contains("nux") || os.contains("aix"));
    }

    static void showHelp(Options options) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("java -jar " + Preferences.JAR_NAME, options);
    }

    static void showVersion() {
        System.out.println(fmt(TranslatedStrings.VERSION_INFO, Preferences.VERSION));
    }

    /**
     * Setting up options for Command Line Interface
     *
     * @return
     */
    static Options addOptions() {
        Options options = new Options();

        Option email = Option.builder().argName("email").hasArg().longOpt(ArgumentNameConstants.EMAIL)
                .desc(t(TranslatedStrings.EMAIL_ARG_DESC)).build();

        Option token = Option.builder().argName("code").hasArg().longOpt(ArgumentNameConstants.TOKEN_GRANTING_CODE)
                .desc(t(TranslatedStrings.TOKEN_GRANTING_CODE_ARG_DESC)).build();

        Option clear = Option.builder().longOpt(ArgumentNameConstants.CLEAR)
                .desc(t(TranslatedStrings.CLEAR_ARG_DESC)).build();

        Option upload = Option.builder().longOpt(ArgumentNameConstants.UPLOAD)
                .desc(t(TranslatedStrings.UPLOAD_ARG_DESC)).build();

        Option rollback = Option.builder().longOpt(ArgumentNameConstants.ROLLBACK)
                .desc(t(TranslatedStrings.ROLLBACK_ARG_DESC)).build();

        Option help = Option.builder().longOpt(ArgumentNameConstants.HELP).desc(t(TranslatedStrings.HELP_ARG_DESC))
                .build();

        Option version = Option.builder().longOpt(ArgumentNameConstants.VERSION)
                .desc(t(TranslatedStrings.VERSION_ARG_DESC)).build();

        Option install_root = Option.builder().hasArg().argName("path").longOpt(ArgumentNameConstants.INSTALL_ROOT)
                .desc(t(TranslatedStrings.INSTALL_ROOT_ARG_DESC)).build();

        Option no_ui = Option.builder().longOpt(ArgumentNameConstants.NO_UI)
                .desc(t(TranslatedStrings.NO_UI_ARG_DESC)).build();

        options.addOption(email);
        options.addOption(token);
        options.addOption(clear);
        options.addOption(upload);
        options.addOption(rollback);
        options.addOption(help);
        options.addOption(version);
        options.addOption(install_root);
        options.addOption(no_ui);

        return options;
    }

    private boolean perhapsHasToken() {
        File tokenFile = AppCfgWrapper.locateTokenFile();
        if (tokenFile == null) {
            return false;
        }
        return tokenFile.exists();
    }

    private EffectiveArgumentValues getArgs() {
        // execute appCfg
        EffectiveArgumentValues args = UpdaterCLI.getArgs(cmd);

        args.noGUI = false;

        // override if different
        if (txtEmail.getText() != null && txtEmail.getText().trim().length() > 0) {
            args.email = txtEmail.getText().trim();
        }

        // override if different
        if (txtToken.getText() != null && txtToken.getText().trim().length() > 0) {
            args.token_granting_code = txtToken.getText().trim();
        }
        return args;
    }

    protected void setActiveHandler(StateExecuteResultHandler activeHandler) {
        this.activeHandler = activeHandler;
    }

    protected void abortAction() {
        if (this.activeHandler != null) {
            this.activeHandler.errorState = StepState.ABORTED;
            this.activeHandler.successState = StepState.ABORTED;
        }
    }

    protected boolean hasActiveHandler() {
        return (this.activeHandler != null);
    }

    private void updateUI() {
        boolean inProgress = hasActiveHandler();
        // upon getting a response...
        if ((txtEmail.getText().length() > 0) && ((txtToken.getText().length() > 0) || perhapsHasToken())) {
            lblWarning.setText(t(TranslatedStrings.WARNING_ERRANT_LABEL));
            if (perhapsHasToken()) {
                btnChoose.setText(t(TranslatedStrings.VERIFY_TOKEN_LABEL));
            } else {
                btnChoose.setText(t(TranslatedStrings.SET_TOKEN_LABEL));
            }
        } else {
            lblWarning.setText(t(TranslatedStrings.WARNING_REDIRECT_LABEL));
            btnChoose.setText(t(TranslatedStrings.GET_TOKEN_LABEL));
        }
        btnDeleteToken.setEnabled(!inProgress && perhapsHasToken());
        btnChoose.setEnabled(!inProgress && txtEmail.getText().length() > 0);
        btnUpload.setEnabled(!inProgress && (txtEmail.getText().length() > 0) && perhapsHasToken());
        btnRollback.setEnabled(!inProgress && (txtEmail.getText().length() > 0) && perhapsHasToken());
    }

    @Override
    public void windowOpened(WindowEvent e) {

    }

    @Override
    public void windowClosing(WindowEvent e) {

    }

    @Override
    public void windowClosed(WindowEvent e) {

    }

    @Override
    public void windowIconified(WindowEvent e) {

    }

    @Override
    public void windowDeiconified(WindowEvent e) {

    }

    @Override
    public void windowActivated(WindowEvent e) {

    }

    @Override
    public void windowDeactivated(WindowEvent e) {

    }

    @EventSubscriber(eventClass = TokenRequestEvent.class)
    public void displayTokenInputField(TokenRequestEvent event) {
        TokenEntryDialog ted = new TokenEntryDialog((JFrame) SwingUtilities.getRoot(txtToken));
        int retVal = ted.showDialog();
        if (retVal == JFileChooser.APPROVE_OPTION) {
            String token = ted.getTokenValue();
            txtToken.setText(token);
            try {
                event.stream.emitToken(token);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            abortAction();
            try {
                event.stream.emitToken("\u0003" + System.lineSeparator());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @EventSubscriber(eventClass = PublishOutputEvent.class)
    public void displayOutput(PublishOutputEvent event) {
        displayOutput(event.type, event.action.name(), event.line);
    }

    public synchronized void displayOutput(StreamType type, String actionName, String line) {
        Document doc = editorArea.getDocument();

        String str = actionName + ((type == StreamType.ERR) ? "!:  " : " :  ") + line + "\r\n";

        try {
            doc.insertString(doc.getLength(), str, null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    enum StepState {
        GET_TOKEN, VERIFY_TOKEN, UPDATE, ROLLBACK, DONE, ABORTED
    }

    class DeleteTokenActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            File tokenFile = AppCfgWrapper.locateTokenFile();
            if (tokenFile.exists()) {
                tokenFile.delete();
            }
            txtToken.setText("");
            updateUI();
        }
    }

    class StateExecuteResultHandler implements ExecuteResultHandler {

        ExecuteStreamHandler streamHandler;
        StepState successState;
        StepState errorState;

        StateExecuteResultHandler(StepState successState, StepState errorState) {
            this.successState = successState;
            this.errorState = errorState;
        }

        @Override
        public void onProcessComplete(int exitValue) {
            // execute appCfg
            EffectiveArgumentValues args = getArgs();
            StateExecuteResultHandler executionHandler;
            StepState nextState;
            switch (successState) {
            case GET_TOKEN:
                // odd state to be in
                setActiveHandler(null);
                updateUI();
                break;
            case VERIFY_TOKEN:
                // verify the token; if ok, then list backends
                executionHandler = new StateExecuteResultHandler(StepState.UPDATE, StepState.ABORTED);
                setActiveHandler(executionHandler);
                updateUI();
                AppCfgWrapper.getToken(args, executionHandler);
                break;
            case UPDATE:
                // update
                executionHandler = new StateExecuteResultHandler(StepState.DONE, StepState.ABORTED);
                setActiveHandler(executionHandler);
                updateUI();
                AppCfgWrapper.update(args, executionHandler);
                break;
            case ROLLBACK:
                // rollback
                executionHandler = new StateExecuteResultHandler(StepState.DONE, StepState.ABORTED);
                setActiveHandler(executionHandler);
                updateUI();
                AppCfgWrapper.rollback(args, executionHandler);
                break;
            case DONE:
                setActiveHandler(null);
                // we need to publish because we need these to be emitted in-order
                EventBus.publish(new PublishOutputEvent(StreamType.OUT, AppCfgActions.status,
                        t(TranslatedStrings.SUCCEEDED_ACTION)));
                updateUI();
                break;
            case ABORTED:
                setActiveHandler(null);
                updateUI();
                break;
            }
        }

        @Override
        public void onProcessFailed(ExecuteException e) {
            setActiveHandler(null);
            // we need to publish because we need these to be emitted in-order
            EventBus.publish(new PublishOutputEvent(StreamType.OUT, AppCfgActions.status,
                    t(TranslatedStrings.ABORTED_BY_USER_ACTION)));
            updateUI();
        }

        @Override
        public ExecuteStreamHandler getExecuteStreamHandler() {
            return streamHandler;
        }

        @Override
        public void setExecuteStreamHandler(ExecuteStreamHandler obj) {
            streamHandler = obj;
        }
    }

    class GetTokenActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            // execute appCfg
            EffectiveArgumentValues args = getArgs();

            StateExecuteResultHandler executionHandler = new StateExecuteResultHandler(StepState.DONE,
                    StepState.ABORTED);
            setActiveHandler(executionHandler);
            updateUI();

            AppCfgWrapper.getToken(args, executionHandler);
        }
    }

    class UploadActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            // execute appCfg
            EffectiveArgumentValues args = getArgs();

            StateExecuteResultHandler executionHandler = new StateExecuteResultHandler(StepState.DONE,
                    StepState.ABORTED);
            setActiveHandler(executionHandler);
            updateUI();

            AppCfgWrapper.update(args, executionHandler);
        }
    }

    class RollbackActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            // execute appCfg
            EffectiveArgumentValues args = getArgs();

            StateExecuteResultHandler executionHandler = new StateExecuteResultHandler(StepState.DONE,
                    StepState.ABORTED);
            setActiveHandler(executionHandler);
            updateUI();

            AppCfgWrapper.rollback(args, executionHandler);
        }
    }

}