org.globus.workspace.cloud.client.CloudClient.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.workspace.cloud.client.CloudClient.java

Source

/*
 * Copyright 1999-2008 University of Chicago
 *
 * 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.globus.workspace.cloud.client;

import org.apache.axis.message.addressing.EndpointReferenceType;
import org.apache.axis.types.URI;
import org.apache.commons.cli.ParseException;
import org.globus.gsi.GlobusCredential;
import org.globus.workspace.client_common.BaseClient;
import org.globus.workspace.client_core.ExecutionProblem;
import org.globus.workspace.client_core.ExitNow;
import org.globus.workspace.client_core.ParameterProblem;
import org.globus.workspace.client_core.actions.Status_QueryAll;
import org.globus.workspace.client_core.print.PrCodes;
import org.globus.workspace.client_core.repr.Workspace;
import org.globus.workspace.client_core.utils.NimbusCredential;
import org.globus.workspace.cloud.client.cluster.ClusterMember;
import org.globus.workspace.cloud.client.cluster.ClusterUtil;
import org.globus.workspace.cloud.client.cluster.KnownHostsTask;
import org.globus.workspace.cloud.client.security.CertUtil;
import org.globus.workspace.cloud.client.security.SecurityPrinter;
import org.globus.workspace.cloud.client.security.TrustedCAs;
import org.globus.workspace.cloud.client.util.CloudClientUtil;
import org.globus.workspace.cloud.client.util.DeploymentXMLUtil;
import org.globus.workspace.cloud.client.util.ExecuteUtil;
import org.globus.workspace.cloud.client.util.RepositoryInterface;
import org.globus.workspace.cloud.client.util.FileListing;
import org.globus.workspace.cloud.client.util.HistoryUtil;
import org.globus.workspace.cloud.client.util.MetadataXMLUtil;
import org.globus.workspace.common.SecurityUtil;
import org.globus.workspace.common.client.CLIUtils;
import org.globus.workspace.common.client.CommonPrint;
import org.globus.workspace.common.print.Print;
import org.globus.workspace.common.print.PrintOpts;
import org.globus.workspace.status.client.WorkspaceStatusClient;
import org.globus.wsrf.impl.security.authorization.HostAuthorization;
import org.globus.wsrf.impl.security.authorization.IdentityAuthorization;
import org.globus.wsrf.utils.AddressingUtils;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Cloudcluster_Type;
import org.nimbustools.messaging.gt4_0.common.CommonUtil;
import org.nimbustools.messaging.gt4_0.generated.metadata.VirtualWorkspace_Type;
import org.nimbustools.messaging.gt4_0.generated.negotiable.WorkspaceDeployment_Type;
import org.nimbustools.messaging.gt4_0.generated.status.WorkspaceStatusFault;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.security.NoSuchAlgorithmException;
import java.util.List;

public class CloudClient {

    // -------------------------------------------------------------------------
    // STATIC VARIABLES
    // -------------------------------------------------------------------------

    public static final int SUCCESS_EXIT_CODE = 0;
    public static final int COMMAND_LINE_EXIT_CODE = 1;
    public static final int APPLICATION_EXIT_CODE = 2;
    public static final int UNKNOWN_ERROR_EXIT_CODE = 3;

    // -------------------------------------------------------------------------
    // INSTANCE VARIABLES
    // -------------------------------------------------------------------------

    private AllArgs args;

    // set if handle is a cluster handle
    private boolean isClusterHandle;

    private ExecuteUtil executeUtil = new ExecuteUtil();

    private final Print print;

    /* derived */
    private GlobusCredential x509Used;
    private String specificEPRpath;
    private String[] trustedCertDirs;
    private String workspaceFactoryURL;
    private EndpointReferenceType statusServiceEPR;
    private String newUnpropTargetURL;
    private ClusterMember[] clusterMembers;
    private URI kernelURI;
    private RepositoryInterface repoUtil;

    // -------------------------------------------------------------------------
    // CONSTRUCTORS
    // -------------------------------------------------------------------------

    /**
     * @param pr must be non-null
     */
    public CloudClient(Print pr) {
        if (pr == null) {
            throw new IllegalArgumentException("print may not be null");
        }
        this.print = pr;
    }

    // -------------------------------------------------------------------------
    // ENTRY POINTS
    // -------------------------------------------------------------------------

    // null means all
    public static int[] getOptInPrCodes() {
        final int[] optOuts = { PrCodes.OPTIONALPARAM__FILE_READ, PrCodes.DEPREQ__FILE_READ,
                PrCodes.METADATA__FILE_READ, PrCodes.SSH__FILE_READ,
                PrCodes.LISTENER_STATECHANGE__INSTANCE_STATE_CHANGE, PrCodes.CREATE__EPRFILE_WRITES,
                PrCodes.CREATE__INSTANCE_ID_PRINT, PrCodes.CREATE__INSTANCE_CREATING_INIITIAL_DURATION,
                PrCodes.CREATE__INSTANCE_CREATING_NET_ASSOCIATION, PrCodes.CREATE__INSTANCE_CREATING_NET_BROADCAST,
                PrCodes.CREATE__INSTANCE_CREATING_NET_GATEWAY, PrCodes.CREATE__INSTANCE_CREATING_NET_MAC,
                PrCodes.CREATE__INSTANCE_CREATING_NET_MASK, PrCodes.CREATE__INSTANCE_CREATING_NET_NETWORK,
                PrCodes.CREATE__INSTANCE_CREATING_NET_NAME, PrCodes.CREATE__ENSEMBLE_ID_PRINT,
                PrCodes.CREATE__CONTEXT_ID_PRINT, PrCodes.CREATE__EXTRALINES, PrCodes.MD_SSH__FILE_READ,
                PrCodes.MD_USERDATA__FILE_READ, PrCodes.LISTENER_TERMINATION__INSTANCE_ID_PRINT,
                PrCodes.LISTENER_AUTODESTROY, PrCodes.CREATE__CTXBROKER_CONTACTINF };
        return PrCodes.getAllCodesExcept(optOuts);
    }

    public static void main(String[] argv) {
        // look for debug early, for diagnosing problems with parsing etc.
        PrintStream debug = null;
        if (CLIUtils.containsDebug(argv)) {
            debug = System.err;
        }
        final PrintOpts pOpts = new PrintOpts(getOptInPrCodes());
        final Print print = new Print(pOpts, System.out, System.err, debug);
        final int retCode = mainImpl(argv, print);
        print.flush();
        print.close();
        System.exit(retCode);
    }

    public static int mainImpl(String[] argv, Print pr) {

        final CloudClient client = new CloudClient(pr);

        // used:
        ParameterProblem parameterProblem = null;
        ExitNow exitNow = null;
        Throwable any = null;

        // unused currently:
        //Throwable throwable = null;
        //ExecutionProblem executionProblem = null;

        int retCode;
        try {
            retCode = _mainImpl(argv, client);
        } catch (ExitNow e) {
            exitNow = e;
            any = e;
            retCode = exitNow.exitCode;
        } catch (ParameterProblem e) {
            parameterProblem = e;
            any = e;
            retCode = BaseClient.COMMAND_LINE_EXIT_CODE;
        } catch (ExecutionProblem e) {
            //executionProblem = e;
            any = e;
            retCode = BaseClient.APPLICATION_EXIT_CODE;
        } catch (Throwable t) {
            //throwable = t;
            any = t;
            retCode = BaseClient.UNKNOWN_EXIT_CODE;
        }

        if (!pr.enabled()) {
            // the rest of this method is for printing
            return retCode; // *** EARLY RETURN ***
        }

        if (exitNow != null) {
            pr.debugln("[exiting via exitnow system]");
            pr.debugln(BaseClient.retCodeDebugStr(retCode));
            return retCode; // *** EARLY RETURN ***
        }

        if (any == null) {
            pr.debugln(BaseClient.retCodeDebugStr(retCode));
            return retCode; // *** EARLY RETURN ***
        }

        CommonPrint.printDebugSection(pr, "PROBLEM");

        final String message = CommonUtil.genericExceptionMessageWrapper(any);

        String err = "Problem: " + message;

        if (parameterProblem != null && !pr.useLogging()) {
            err += "\nSee help (-h).";
        }

        pr.errln(err);

        pr.debugln("\n");

        final String sectionTitle = "STACKTRACE";
        CommonPrint.printDebugSection(pr, sectionTitle);

        any.printStackTrace(pr.getDebugProxy());

        CommonPrint.printDebugSectionEnd(pr, sectionTitle);

        pr.debugln("\n");

        pr.debugln("Stacktrace was from: " + any.getMessage());

        pr.debugln(BaseClient.retCodeDebugStr(retCode));

        return retCode;
    }

    private static int _mainImpl(String[] argv, CloudClient client)

            throws ParameterProblem, ExecutionProblem, ExitNow {

        // (for development only, to attach a remote debugger etc)
        if (CLIUtils.containsDebuggerHang(argv)) {
            try {
                CLIUtils.hangForInput(client.getPrint());
            } catch (IOException e) {
                throw new ExecutionProblem("", e);
            }
        }

        CommonPrint.logArgs(argv, client.getPrint());

        final AllArgs allArgs = new AllArgs(client.getPrint());

        try {
            allArgs.intakeCmdlineOptions(argv);
        } catch (ParseException e) {
            // in all likelihood due to issue with given parameter
            throw new ParameterProblem(e.getMessage(), e);
        }

        try {
            allArgs.intakeUserProperties();
            allArgs.intakeDefaultProperties();
        } catch (IOException e) {
            // in all likelihood due to issue with given parameter
            throw new ParameterProblem(e.getMessage(), e);
        }

        client.run(allArgs);

        return BaseClient.SUCCESS_EXIT_CODE;
    }

    // -------------------------------------------------------------------------
    // RUN() WRAPPER
    // -------------------------------------------------------------------------

    /**
     * @param allArguments may not be null
     * @throws ParameterProblem problem with inputs
     * @throws ExecutionProblem problem going through with something
     * @throws ExitNow early exit (see source code...)
     */
    public void run(AllArgs allArguments) throws ParameterProblem, ExecutionProblem, ExitNow {

        if (allArguments == null) {
            throw new IllegalArgumentException("allArguments may not be null");
        }

        this.args = allArguments;

        String repoType = this.args.getXferType();
        if (repoType == null) {
            repoType = "gridftp";
        }
        this.repoUtil = CloudClientUtil.getRepoUtil(repoType, this.args, this.print);

        try {
            final String credPath = this.args.getNimbusCertFile();
            if (credPath != null) {
                NimbusCredential.setNimbusCredentialPath(credPath);
            }
            final String keyPath = this.args.getNimbusKeyFile();
            if (keyPath != null) {
                NimbusCredential.setNimbusUnencryptedKeyPath(keyPath);
            }

            this.parameterCheck();
        } catch (Exception e) {
            throw new ParameterProblem(e.getMessage(), e);
        }

        try {
            this.runNoParameterCheck();
        } finally {
            if (this.executeUtil != null) {
                this.executeUtil.stopExecutorService();
            }
        }
    }

    protected Print getPrint() {
        return this.print;
    }

    // -------------------------------------------------------------------------
    // PARAMETER CHECKS
    // -------------------------------------------------------------------------

    void parameterCheck() throws Exception {

        final List actions = this.args.getActions();

        if (actions == null || actions.isEmpty()) {
            throw new ParameterProblem("Give at least one action.");
        }

        if (actions.contains(AllArgs.ACTION_HELP) || actions.contains(AllArgs.ACTION_EXTRAHELP)
                || actions.contains(AllArgs.ACTION_USAGE)) {
            // nothing else matters
            return; // *** EARLY RETURN ***
        }

        if (actions.contains(AllArgs.ACTION_SECURITY_PRINT)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.SECURITY_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_securityPrint();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_TARGET_PRINT)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.PRINT_TARGET_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_targetPrint();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_HASH_PRINT)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.HASH_PRINT_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_hashPrint();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_LIST)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.LIST_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_list();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_TRANSFER)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.TRANSFER_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_transfer();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_DELETE)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.DELETE_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_delete();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_DOWNLOAD)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.DOWNLOAD_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_download();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_SERVICE_PRINT)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.PRINT_SERVICE_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_servicePrint();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_DESTROY)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.DESTROY_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_destroy();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_STATUS_CHECK)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.STATUS_CHECK_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_status();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_ASSOC_QUERY)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.ASSOC_QUERY_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_assocquery();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_SAVE)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.SAVE_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_save();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_RUN)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.RUN_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_run();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_INIT_CONTEXT)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.INIT_CTX_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_initCtx();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }

        if (actions.contains(AllArgs.ACTION_PRINT_CTX_STATUS)) {
            final String sectionTitle = "PARAMETER CHECK: --" + Opts.PRINT_CTX_STATUS_OPT_STRING;
            CommonPrint.printDebugSection(this.print, sectionTitle);
            this.parameterCheck_printContextStatus();
            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }

    void parameterCheck_initCtx() throws ParameterProblem {
        this._parameterCheck_clusterfile();
        this._parameterCheck_initContext();
    }

    void parameterCheck_list() throws ParameterProblem {

        this.repoUtil.paramterCheck(this.args, Opts.LIST_OPT_STRING);
    }

    void parameterCheck_hashPrint() throws ParameterProblem {

        if (this.args.getHashPrintDN() == null) {
            throw new ParameterProblem("Hash print requires DN to hash");
        }
    }

    void parameterCheck_securityPrint() throws ParameterProblem {
        CloudClientUtil.checkX509Credential("Security printing", this.print);
        try {
            loadX509BeingUsed();
        } catch (Exception e) {
            throw new ParameterProblem(e.getMessage());
        }
    }

    void parameterCheck_servicePrint() throws ParameterProblem {
        this._checkServiceURL(Opts.PRINT_SERVICE_OPT_STRING);
    }

    void parameterCheck_destroy() throws ParameterProblem {

        if (this.args.getActions().contains(AllArgs.ACTION_RUN)) {
            throw new ParameterProblem(
                    "You cannot create (--" + Opts.RUN_OPT_STRING + ") and destroy in the same invocation");
        }

        final String actionString = "Terminating";
        CloudClientUtil.checkX509Credential(actionString, this.print);
        this._translateHandle(actionString);
        this._checkSpecificEPR(actionString);
    }

    void parameterCheck_status() throws ParameterProblem {

        if (this.args.getActions().contains(AllArgs.ACTION_RUN)) {
            throw new ParameterProblem("You cannot create (--" + Opts.RUN_OPT_STRING
                    + ") and run a status check (on a previously" + "created workspace) in the same invocation");
        }

        final String actionString = "Checking status";
        CloudClientUtil.checkX509Credential(actionString, this.print);
        this._translateHandle(actionString);

        if (this.args.getHistorySubDir() != null || this.args.getEprGivenFilePath() != null) {
            this._checkSpecificEPR("Checking status of one workspace");
        } else {
            this._checkStatusServiceEPR("Checking status of all workspaces");
        }
    }

    void parameterCheck_printContextStatus() throws ParameterProblem {

        if (this.args.getActions().contains(AllArgs.ACTION_RUN)) {
            throw new ParameterProblem("You cannot create (--" + Opts.RUN_OPT_STRING
                    + ") and run print context status (on a previously"
                    + "created cluster) in the same invocation");
        }

        final String actionString = "Printing ctx status of each node";
        CloudClientUtil.checkX509Credential(actionString, this.print);
        this._translateHandle(actionString);

        if (this.args.getHistorySubDir() != null || this.args.getEprGivenFilePath() != null) {
            this._checkSpecificBrokerEPR(actionString);
        } else {
            throw new ParameterProblem(actionString + " requires either " + "'--" + Opts.HANDLE_OPT_STRING
                    + "' or path to specific context EPR file using '--" + Opts.EPR_FILE_OPT_STRING + '\'');
        }
    }

    void parameterCheck_assocquery() throws ParameterProblem {
        final String actionString = "Querying networks";
        CloudClientUtil.checkX509Credential(actionString, this.print);
        this._checkServiceURL(actionString);
    }

    void parameterCheck_save() throws ParameterProblem {

        if (this.args.getActions().contains(AllArgs.ACTION_RUN)) {
            throw new ParameterProblem("You cannot create (--" + Opts.RUN_OPT_STRING + ") and "
                    + "save a previously created workspace in the same invocation");
        }

        final String actionString = "Saving";
        CloudClientUtil.checkX509Credential(actionString, this.print);
        this._translateHandle(actionString);
        this._checkSpecificEPR(actionString);
        this.repoUtil.paramterCheck(this.args, actionString);

        final String newname = this.args.getNewname();
        if (newname != null) {
            this.print.debugln("save called with newname '" + newname + "'");
            try {
                this.newUnpropTargetURL = this.repoUtil.getDerivedImageURL(newname);
            } catch (Exception e) {
                throw new ParameterProblem("Problem with save's newname '" + newname + "': " + e.getMessage(), e);
            }
        } else {
            this.print.debugln("save called with no newname");
        }
    }

    void parameterCheck_targetPrint() throws ParameterProblem {

        this.repoUtil.paramterCheck(this.args, Opts.TARGETDIR_OPT_STRING);

        final String sourcefile = this.args.getSourcefile();
        final String name = this.args.getName();
        if (sourcefile == null && name == null) {
            throw new ParameterProblem(Opts.TARGETDIR_OPT_STRING + " requires either '" + Opts.SOURCEFILE_OPT_STRING
                    + "' or '" + Opts.NAME_OPT_STRING + "'");
        }
    }

    void parameterCheck_transfer() throws ParameterProblem {

        this.repoUtil.paramterCheck(this.args, Opts.TRANSFER_OPT_STRING);

        final String sourcefile = this.args.getSourcefile();
        if (sourcefile == null) {
            throw new ParameterProblem(Opts.TRANSFER_OPT_STRING + " requires '" + Opts.SOURCEFILE_OPT_STRING + "'");
        }

        this._checkSourcefile();
    }

    void parameterCheck_delete() throws ParameterProblem {

        this.repoUtil.paramterCheck(this.args, Opts.DELETE_OPT_STRING);

        final String name = this.args.getName();
        if (name == null) {
            throw new ParameterProblem("Deleting requires '" + Opts.NAME_OPT_STRING + "' (the name "
                    + "of the file in your remote personal directory)");
        }
    }

    void parameterCheck_download() throws ParameterProblem {

        this.repoUtil.paramterCheck(this.args, Opts.DOWNLOAD_OPT_STRING);

        final String localfile = this.args.getLocalfile();
        final String name = this.args.getName();

        if (localfile == null) {
            throw new ParameterProblem(Opts.DOWNLOAD_OPT_STRING + " requires '" + Opts.LOCAL_FILE_OPT_STRING + "'");
        }

        if (name == null) {
            throw new ParameterProblem("Downloading requires '" + Opts.NAME_OPT_STRING + "' (the name "
                    + "of the file in your remote personal directory)");
        }

        this._checkLocalfile();
    }

    /* SINGLE RUN/CLUSTER RUN CHECKS: */

    void parameterCheck_run() throws ParameterProblem {

        final String sourcefile = this.args.getSourcefile();
        final String clusterPath = this.args.getClusterPath();
        final String name = this.args.getName();
        final String ec2ScriptPath = this.args.getEc2ScriptPath();

        // Differentiate between run and cluster-run

        if (sourcefile == null && name == null && clusterPath == null) {

            throw new ParameterProblem("Running requires either '" + Opts.SOURCEFILE_OPT_STRING + "', '"
                    + Opts.NAME_OPT_STRING + "', or '" + Opts.CLUSTER_OPT_STRING + "'");
        }

        if (sourcefile != null && name != null) {
            throw new ParameterProblem("You may not specify both '" + Opts.SOURCEFILE_OPT_STRING + "' and '"
                    + Opts.NAME_OPT_STRING + "'");
        }

        if (sourcefile == null && name == null && ec2ScriptPath != null) {
            this.args.getActions().add(AllArgs.ACTION_EC2_CLUSTER);
            this.print.debugln("\n*** EC2 CLUSTER HELPER\n");
            this._parameterCheck_runec2cluster();
            this._parameterCheck_clusterfile();
        } else if (sourcefile == null && name == null) {
            this.args.getActions().add(AllArgs.ACTION_RUN_CLUSTER);
            this.print.debugln("\n*** RUN CLUSTER\n");
            this._parameterCheck_runcommon();
            this._parameterCheck_clusterfile();
        } else {
            this.args.getActions().add(AllArgs.ACTION_RUN_SINGLE);
            this.print.debugln("\n*** RUN SINGLE\n");
            this._parameterCheck_runcommon();
        }
    }

    void _parameterCheck_clusterfile() throws ParameterProblem {

        String clusterPath = this.args.getClusterPath();

        this.clusterMembers = ClusterUtil.getClusterMembers(clusterPath, this.args.getBrokerLocalNicPrefix(),
                this.args.getBrokerPublicNicPrefix(), this.print);

        // true if at least one contextualization section is present
        boolean oneContextualization = false;

        // true if at least one adjustment is requested
        boolean adjustKnownHosts = false;

        String ssh_hostsfile = this.args.getSsh_hostsfile();
        if (ssh_hostsfile == null) {
            this.print.errln("Your SSH known_hosts file will not be updated with "
                    + "generated keys (see cloud.properties to enable).");
        }

        for (int i = 0; i < this.clusterMembers.length; i++) {

            final ClusterMember member = this.clusterMembers[i];

            if (member.isOneLoginFlagPresent()) {
                if (member.getClusterForUserData() == null && ssh_hostsfile != null) {
                    this.print.errln("  - Warning: Host SSH pubkey(s) will " + "not be available for this member "
                            + "(it's not involved in contextualization)");
                } else {
                    adjustKnownHosts = true;
                }
            }

            if (member.getClusterForUserData() != null) {
                oneContextualization = true;
            }
        }

        if (this.args.isNoContextLock() && !oneContextualization) {
            this.print.errln("  - Warning: You gave the --" + Opts.NOCTXLOCK_OPT_STRING + " flag but there are no "
                    + "contextualization sections in the cluster definition.");
        }

        // at least one adjustment is requested
        if (adjustKnownHosts && ssh_hostsfile != null) {

            String newHostsFile = ClusterUtil.expandSshHostsFile(ssh_hostsfile, this.print);
            this.args.setSsh_hostsfile(newHostsFile);
        }

    }

    void _parameterCheck_runcommon() throws ParameterProblem {

        CloudClientUtil.checkX509Credential("Running", this.print);

        this._checkServiceURL(Opts.RUN_OPT_STRING);

        String sshfile = this.args.getSshfile();

        if (sshfile == null) {
            this.print.errln("WARNING: no SSH public key is configured");
        }

        if (this.args.getHistoryDirectory() == null) {
            throw new ParameterProblem("Running requires '" + Opts.HISTORY_DIR_OPT_STRING + "'");
        }

        this._checkHistoryDirectory("Running");

        if (!this.args.isDurationMinutesConfigured()) {
            throw new ParameterProblem("Running requires '" + Opts.HOURS_OPT_STRING + "'");
        }

        this.print.debugln("Checking on repository URL for running "
                + "because we are going to derive propagation string " + "for the client");

        this.repoUtil.paramterCheck(this.args, Opts.RUN_OPT_STRING);

        final String newname = this.args.getNewname();
        if (newname != null) {
            this.print.debugln("run called with newname '" + newname + "'");
            try {
                this.newUnpropTargetURL = this.repoUtil.getDerivedImageURL(newname);

            } catch (Exception e) {
                throw new ParameterProblem("Problem with run's newname '" + newname + "': " + e.getMessage(), e);
            }
        }

        if (sshfile != null) {

            sshfile = CloudClientUtil.expandSshPath(sshfile);

            final File f = new File(sshfile);
            sshfile = f.getAbsolutePath();
            this.args.setSshfile(sshfile);

            this.print.debugln("Examining '" + sshfile + "'");

            if (!CloudClientUtil.fileExistsAndReadable(sshfile)) {
                throw new ParameterProblem(
                        "SSH public key file does not " + "exist or is not readable: '" + sshfile + "'");
            }

            this.print.debugln("Exists and readable: '" + sshfile + "'");
        }

        if (this.args.getKernel() != null) {
            final String kernel = this.args.getKernel().trim();
            if (kernel.length() == 0) {
                throw new ParameterProblem("empty kernel string?");
            }

            if (kernel.indexOf('/') >= 0) {
                throw new ParameterProblem("kernel may not contain any /");
            }
            if (kernel.indexOf("..") >= 0) {
                throw new ParameterProblem("kernel may not contain any '..'");
            }

            // already-propagated kernel is implied
            try {
                this.kernelURI = new URI("file://" + kernel);
            } catch (URI.MalformedURIException e) {
                throw new ParameterProblem(e.getMessage(), e);
            }
        }
    }

    void _parameterCheck_initContext() throws ParameterProblem {
        CloudClientUtil.checkX509Credential("Context init helper (which talks to the " + "context broker)",
                this.print);

        if (this.args.getInitCtxDir() == null) {
            throw new ParameterProblem("Must specify ctx output directory");
        }
    }

    void _parameterCheck_runec2cluster() throws ParameterProblem {

        CloudClientUtil.checkX509Credential("EC2 cluster helper (which talks to the " + "context broker)",
                this.print);

        final String brokerURL = this.args.getBrokerURL();
        if (brokerURL == null) {
            final String factoryHostPort = this.args.getFactoryHostPort();
            if (factoryHostPort == null) {
                throw new ParameterProblem(" requires either '--" + Opts.BROKER_URL_OPT_STRING + "' or '--"
                        + Opts.FACTORY_OPT_STRING + "'");
            }

            final String url = CloudClientUtil.serviceURL(factoryHostPort);
            if (!CloudClientUtil.validURL(url, this.print.getDebugProxy())) {
                throw new ParameterProblem("Derived service URL is not a " + "valid URL: '" + url + "'");
            }

            this.workspaceFactoryURL = url;
        }

        if (this.args.getHistoryDirectory() == null) {
            throw new ParameterProblem("This requires '" + Opts.HISTORY_DIR_OPT_STRING + "'");
        }

        this._checkHistoryDirectory("EC2 cluster helper (which talks to the " + "context broker)");
    }

    /* SHARED CHECKS: */

    void _checkServiceURL(String action) throws ParameterProblem {

        final String factoryHostPort = this.args.getFactoryHostPort();

        if (factoryHostPort == null) {
            throw new ParameterProblem(action + " requires '" + Opts.FACTORY_OPT_STRING + "'");
        }

        final String url = CloudClientUtil.serviceURL(factoryHostPort);
        if (!CloudClientUtil.validURL(url, this.print.getDebugProxy())) {
            throw new ParameterProblem("Service URL is not a valid URL: '" + url + "'");
        }

        this.workspaceFactoryURL = url;
        this.print.debugln("Derived workspace factory URL: '" + url + "'");
    }

    void _checkStatusServiceEPR(String action) throws ParameterProblem {

        final String factoryHostPort = this.args.getFactoryHostPort();

        if (factoryHostPort == null) {
            throw new ParameterProblem(action + " requires '" + Opts.FACTORY_OPT_STRING + "'");
        }

        final String url = CloudClientUtil.statusServiceURL(factoryHostPort);
        if (!CloudClientUtil.validURL(url, this.print.getDebugProxy())) {
            throw new ParameterProblem("Status Service URL is not a valid URL: '" + url + "'");
        }

        try {
            this.statusServiceEPR = AddressingUtils.createEndpointReference(url,
                    WorkspaceStatusClient.defaultResourceKey);
        } catch (Exception e) {
            final String err = "Problem deriving status service EPR: ";
            throw new ParameterProblem(err + e.getMessage(), e);
        }

        this.print.debugln("Derived workspace status service URL: '" + url + "'");
    }

    void _checkHistoryDirectory(String action) throws ParameterProblem {

        final String historyDirectory = this.args.getHistoryDirectory();

        if (historyDirectory == null) {
            throw new ParameterProblem(action + " requires '" + Opts.HISTORY_DIR_OPT_STRING + "'");
        }

        CloudClientUtil.verifyHistoryDir(historyDirectory, true, true);
    }

    void _translateHandle(String action) throws ParameterProblem {

        if (this.args.getHandle() == null) {
            this.print.debugln("no handle supplied");
            return;
        }

        if (this.args.getHistorySubDir() != null) {
            this.print.debugln("handle supplied, but presence of history " + "sub-directory argument overrides it");
            return;
        }

        if (this.args.getHistoryDirectory() == null) {
            throw new ParameterProblem("Not able to use handle to locate the "
                    + "necessary, stored information: no top-level history " + "directory argument was supplied.");
        }

        this._checkHistoryDirectory(action);

        // translate history directory and handle argument into history subdir,
        // the result will be checked later by whatever needs it
        final String historySubDir = this.args.getHistoryDirectory() + File.separator + this.args.getHandle();
        this.args.setHistorySubDir(historySubDir);
    }

    void _checkSpecificEPR(String action)

            throws ParameterProblem {

        final String historySubDir = this.args.getHistorySubDir();
        final String eprGivenFilePath = this.args.getEprGivenFilePath();

        if (historySubDir == null && eprGivenFilePath == null) {

            // This is a message present to human.  An unmentioned, third option
            // is technically possible.  It's more esoteric, but one could
            // specify Opts.HISTORY_SUBDIR_OPT_STRING directly.

            throw new ParameterProblem(action + " requires either " + "'" + Opts.HANDLE_OPT_STRING
                    + "' or path to specific EPR file using '" + Opts.EPR_FILE_OPT_STRING + "'");
        }

        if (historySubDir != null && eprGivenFilePath != null) {
            throw new ParameterProblem(action + " will use either '" + Opts.HISTORY_SUBDIR_OPT_STRING + "' or '"
                    + Opts.EPR_FILE_OPT_STRING + "', but you've provided BOTH options.");
        }

        File f;
        if (historySubDir != null) {
            CloudClientUtil.verifyHistoryDir(historySubDir, false, false);
            f = new File(historySubDir, HistoryUtil.SINGLE_EPR_FILE_NAME);
            if (!CloudClientUtil.fileExistsAndReadable(f)) {
                f = new File(historySubDir, HistoryUtil.ENSEMBLE_EPR_FILE_NAME);
                if (CloudClientUtil.fileExistsAndReadable(f)) {
                    this.isClusterHandle = true;
                } else {
                    throw new ParameterProblem("Cannot find or read any " + "instance or cluster EPRs under '"
                            + historySubDir + "'\nPerhaps this launch did " + "not succeed?");
                }
            }
        } else {
            f = new File(eprGivenFilePath);
            if (!CloudClientUtil.fileExistsAndReadable(f)) {
                throw new ParameterProblem("Cannot find or read '" + eprGivenFilePath + "'");
            }
        }

        String old = null;
        if (this.specificEPRpath != null) {
            old = this.specificEPRpath;
        }

        this.specificEPRpath = f.getAbsolutePath();

        if (old != null) {
            if (!old.equals(this.specificEPRpath)) {
                throw new ParameterProblem(
                        "Unexpected with any input: specific EPR that was " + "previously set is '" + old
                                + "' but it is " + "not the same as a repeat derivation which " + "is '"
                                + this.specificEPRpath + "'. Please " + "send debug output file (in the history "
                                + "directory) to developers, thankyou.");
            }
        }
    }

    void _checkSpecificBrokerEPR(String action)

            throws ParameterProblem {

        final String historySubDir = this.args.getHistorySubDir();
        final String eprGivenFilePath = this.args.getEprGivenFilePath();

        if (historySubDir == null && eprGivenFilePath == null) {

            // This is a message present to human.  An unmentioned, third option
            // is technically possible.  It's more esoteric, but one could
            // specify Opts.HISTORY_SUBDIR_OPT_STRING directly.

            throw new ParameterProblem(action + " requires either " + "'" + Opts.HANDLE_OPT_STRING
                    + "' or path to specific EPR file using '" + Opts.EPR_FILE_OPT_STRING + "'");
        }

        if (historySubDir != null && eprGivenFilePath != null) {
            throw new ParameterProblem(action + " will use either '" + Opts.HISTORY_SUBDIR_OPT_STRING + "' or '"
                    + Opts.EPR_FILE_OPT_STRING + "', but you've provided BOTH options.");
        }

        File f;
        if (historySubDir != null) {
            CloudClientUtil.verifyHistoryDir(historySubDir, false, false);
            f = new File(historySubDir, HistoryUtil.CONTEXT_EPR_FILE_NAME);
            if (!CloudClientUtil.fileExistsAndReadable(f)) {
                throw new ParameterProblem("Cannot find or read any " + "context EPRs under '" + historySubDir
                        + "'\nPerhaps this launch did " + "not succeed or it was not a virtual cluster launch "
                        + "using the context broker?");
            }
        } else {
            f = new File(eprGivenFilePath);
            if (!CloudClientUtil.fileExistsAndReadable(f)) {
                throw new ParameterProblem("Cannot find or read '" + eprGivenFilePath + "'");
            }
        }

        String old = null;
        if (this.specificEPRpath != null) {
            old = this.specificEPRpath;
        }

        this.specificEPRpath = f.getAbsolutePath();

        if (old != null) {
            if (!old.equals(this.specificEPRpath)) {
                throw new ParameterProblem(
                        "Unexpected with any input: specific EPR that was " + "previously set is '" + old
                                + "' but it is " + "not the same as a repeat derivation which " + "is '"
                                + this.specificEPRpath + "'. Please " + "send debug output file (in the history "
                                + "directory) to developers, thankyou.");
            }
        }

    }

    void _checkSourcefile() throws ParameterProblem {
        final String sourcefile = this.args.getSourcefile();
        if (!CloudClientUtil.fileExistsAndReadable(sourcefile)) {
            throw new ParameterProblem(
                    "Specified source file does not " + "exist or is not readable: '" + sourcefile + "'");
        }
    }

    void _checkLocalfile() throws ParameterProblem {
        final String localfile = this.args.getLocalfile();
        if (CloudClientUtil.fileExists(localfile)) {
            throw new ParameterProblem("Specified local target file already " + "exists: '" + localfile + "'");
        }
    }

    void _checkDepReqDefaults(String action) throws ParameterProblem {
        this.__checknull(action, "deployment request file name", this.args.getDeploymentRequest_fileName());
    }

    void _checkMetadataDefaults(String action) throws ParameterProblem {
        this.__checknull(action, "association name", this.args.getMetadata_association());
        this.__checknull(action, "cpu type", this.args.getMetadata_cpuType());
        this.__checknull(action, "metadata filename", this.args.getMetadata_fileName());
        this.__checknull(action, "partition mount-as", this.args.getMetadata_mountAs());
        this.__checknull(action, "NIC name", this.args.getMetadata_nicName());
        this.__checknull(action, "VMM type", this.args.getMetadata_vmmType());
        this.__checknull(action, "VMM version", this.args.getMetadata_vmmVersion());
    }

    void __checknull(String action, String propname, String val) throws ParameterProblem {
        if (val == null) {
            throw new ParameterProblem(action + " requires " + propname);
        }
    }

    /* DERIVED FIELDS */

    // only used for printing and/or presence-detect
    GlobusCredential loadX509BeingUsed() throws Exception {
        if (this.x509Used != null) {
            return this.x509Used;
        }
        this.x509Used = CloudClientUtil.getActiveX509Credential(this.print);
        return this.x509Used;
    }

    // -------------------------------------------------------------------------
    // EXECUTE
    // -------------------------------------------------------------------------

    void runNoParameterCheck() throws ExecutionProblem, ExitNow {

        if (this.action_help()) {
            return; // if a help action ran, cut out early
        }

        this.action_hashPrint();
        this.trustedCAs();
        this.action_justPrinting();
        this.action_assoc_query();
        this.action_list();
        this.action_status();
        this.action_save();
        this.action_destroy();
        this.action_transfer();
        this.action_run_single();
        this.action_run_cluster();
        this.action_run_ec2cluster();
        this.action_init_context();
        this.action_download();
        this.action_delete();
        this.action_print_context_status();
    }

    void trustedCAs() throws ExecutionProblem {

        final String sectionTitle = "TRUSTED CAs CHECK";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        final String caAppendDir = this.args.getCaAppendDir();

        try {
            if (caAppendDir != null) {
                this.print.debugln("caAppendDir = '" + caAppendDir + "'");
                TrustedCAs.addTrustedCaDirectory(caAppendDir);
                this.print.debugln("Added '" + caAppendDir + "' to " + "trusted certificate directories");
            } else {
                this.print.debugln("caAppendDir not present");
            }

            this.trustedCertDirs = CertUtil.trustedCertificateDirectories(this.print.getDebugProxy());
        } catch (Exception e) {
            throw new ExecutionProblem(e.getMessage(), e); // ...
        }

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    // returns true if a help action ran
    boolean action_help() throws ExecutionProblem {
        try {
            if (this.args.getActions().contains(AllArgs.ACTION_USAGE)) {
                this.print.infoln(new Help().getUsageString());
                return true;
            }

            if (this.args.getActions().contains(AllArgs.ACTION_HELP)) {
                this.print.infoln(new Help().getHelpString());
                return true;
            }

            if (this.args.getActions().contains(AllArgs.ACTION_EXTRAHELP)) {
                this.print.infoln(new Help().getExtraHelpString());
                return true;
            }
        } catch (IOException e) {
            throw new ExecutionProblem("Unexpected problem with help system: " + e.getMessage(), e);
        }
        return false;
    }

    void action_hashPrint() throws ExecutionProblem {
        if (this.args.getActions().contains(AllArgs.ACTION_HASH_PRINT)) {
            //this.print.infoln("  DN: " + this.hashPrintDN);
            try {
                //this.print.infoln("HASH: " +
                //            SecurityUtil.hashDN(this.hashPrintDN));
                this.print.infoln(SecurityUtil.hashDN(this.args.getHashPrintDN()));
            } catch (NoSuchAlgorithmException e) {
                throw new ExecutionProblem(e.getMessage(), e);
            }
        }
    }

    void action_justPrinting() throws ExecutionProblem {
        try {
            if (this.args.getActions().contains(AllArgs.ACTION_SECURITY_PRINT)) {

                final String sectionTitle = "ACTION: SECURITY PRINT";
                CommonPrint.printDebugSection(this.print, sectionTitle);

                final SecurityPrinter sp = new SecurityPrinter(this.x509Used, this.trustedCertDirs);
                sp.print(this.args.getCaHash(), this.print.getInfoProxy(), this.print.getDebugProxy());

                CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
            }

            if (this.args.getActions().contains(AllArgs.ACTION_SERVICE_PRINT)) {

                final String sectionTitle = "ACTION: SERVICE PRINT";
                CommonPrint.printDebugSection(this.print, sectionTitle);

                this.print.infoln(this.workspaceFactoryURL);

                CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
            }

            if (this.args.getActions().contains(AllArgs.ACTION_TARGET_PRINT)) {

                final String sectionTitle = "ACTION: TARGET URL PRINT";
                CommonPrint.printDebugSection(this.print, sectionTitle);

                String sourcefile = this.args.getSourcefile();
                String name = this.args.getName();

                if (sourcefile != null) {
                    final File f = new File(sourcefile);
                    name = f.getName();
                }

                if (name != null) {
                    String pUrl = this.repoUtil.getRemoteUrl(name);
                    this.print.infoln(pUrl);
                } else {
                    throw new ExecutionProblem("print target configured but no target URL?");
                }

                CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
            }
        } catch (Exception e) {
            throw new ExecutionProblem(e.getMessage(), e); // ...
        }
    }

    void action_list() throws ExecutionProblem {
        if (this.args.getActions().contains(AllArgs.ACTION_LIST)) {
            final String sectionTitle = "ACTION: LIST";
            CommonPrint.printDebugSection(this.print, sectionTitle);

            final FileListing[] files = this.repoUtil.listFiles(this.print.getInfoProxy(), this.print.getErrProxy(),
                    this.print.getDebugProxy());

            CloudClientUtil.printFileList(files, this.print);

            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }

    void action_destroy() throws ExecutionProblem, ExitNow {
        if (this.args.getActions().contains(AllArgs.ACTION_DESTROY)) {
            final String sectionTitle = "ACTION: DESTROY";
            CommonPrint.printDebugSection(this.print, sectionTitle);

            this.executeUtil.destroy(this.specificEPRpath, this.args.getFactoryID(), this.isClusterHandle,
                    this.print);

            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }

    void action_status() throws ExecutionProblem, ExitNow {
        if (this.args.getActions().contains(AllArgs.ACTION_STATUS_CHECK)) {
            if (this.specificEPRpath == null) {
                this._action_status_all();
            } else {
                this._action_status_one();
            }
        }
    }

    private void _action_status_one() throws ExecutionProblem, ExitNow {
        final String sectionTitle = "ACTION: STATUS CHECK (one workspace)";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        this.executeUtil.statusQuery(this.specificEPRpath, this.args.getFactoryID(), this.print);

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    private void _action_status_all() throws ExecutionProblem, ExitNow {
        final String sectionTitle = "ACTION: STATUS CHECK (all workspaces)";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        final StubConf conf = new StubConf();

        conf.setEPR(this.statusServiceEPR);

        if (this.args.getFactoryID() != null) {
            conf.setAuthorization(new IdentityAuthorization(this.args.getFactoryID()));
        } else {
            conf.setAuthorization(HostAuthorization.getInstance());
        }

        final Status_QueryAll queryAll = new Status_QueryAll(this.statusServiceEPR, conf, this.print);

        this.print.infoln("Querying for ALL instances.\n");

        try {
            final Workspace[] workspaces = queryAll.queryAll();
            CloudClientUtil.printCurrent(workspaces, this.print, this.args.getHistoryDirectory(),
                    this.statusServiceEPR);
        } catch (WorkspaceStatusFault e) {
            throw new ExecutionProblem(e.getMessage(), e);
        } catch (ParameterProblem e) {
            throw new ExecutionProblem(e.getMessage(), e);
        }

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    void action_save() throws ExecutionProblem, ExitNow {
        if (this.args.getActions().contains(AllArgs.ACTION_SAVE)) {
            final String sectionTitle = "ACTION: SAVE";
            CommonPrint.printDebugSection(this.print, sectionTitle);

            this.executeUtil.save(this.args.getNewname(), this.newUnpropTargetURL, this.specificEPRpath,
                    this.args.getPollMs(), this.args.isUseNotifications(), this.args.getFactoryID(), this.print);

            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }

    void action_transfer() throws ExecutionProblem {
        if (this.args.getActions().contains(AllArgs.ACTION_TRANSFER)) {
            final String sectionTitle = "ACTION: TRANSFER";
            CommonPrint.printDebugSection(this.print, sectionTitle);

            final String sourcefile = this.args.getSourcefile();
            final File f = new File(sourcefile);
            this.repoUtil.uploadVM(sourcefile, f.getName(), this.print.getInfoProxy(), this.print.getDebugProxy(),
                    this.executeUtil.getExecer());

            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }

    void action_assoc_query() throws ExecutionProblem, ExitNow {
        if (!this.args.getActions().contains(AllArgs.ACTION_ASSOC_QUERY)) {
            return; // *** EARLY RETURN ***
        }

        final String sectionTitle = "ACTION: NETWORK QUERY";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        this.executeUtil.associationQuery(this.workspaceFactoryURL, this.args.getFactoryID(), this.print);

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    void action_run_single() throws ExecutionProblem, ExitNow {

        if (!this.args.getActions().contains(AllArgs.ACTION_RUN_SINGLE)) {
            return; // *** EARLY RETURN ***
        }

        final String sectionTitle = "ACTION: RUN SINGLE";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        String imageName = this.args.getName();

        if (imageName == null) {
            final File sfile = new File(this.args.getSourcefile());
            imageName = sfile.getName();

            this.print.debugln("Derived image name to run from " + "given sourcefile: '" + imageName + "'");

        } else {
            this.print.debugln("Using given image name: '" + imageName + "'");
        }

        final String imageURL;
        try {
            imageURL = this.repoUtil.getDerivedImageURL(imageName);
        } catch (Exception e) {
            throw new ExecutionProblem("Problem with image name '" + imageName + "': " + e.getMessage(), e);
        }

        final URI imageURI;
        try {
            imageURI = new URI(imageURL);
        } catch (URI.MalformedURIException e) {
            throw new ExecutionProblem(e.getMessage(), e);
        }

        final String[] associations = { this.args.getMetadata_association() };
        final String[] nicnames = { this.args.getMetadata_nicName() };

        // runName (3rd method paramater, being set to null) will be set in
        // startOneWorkspace once rundir is known
        final VirtualWorkspace_Type metadata = MetadataXMLUtil.constructMetadata(imageURI,
                this.args.getMetadata_mountAs(), null, associations, nicnames, this.args.getMetadata_cpuType(),
                this.args.getMetadata_vmmVersion(), this.args.getMetadata_vmmType(), this.kernelURI);

        final WorkspaceDeployment_Type deploymentRequest = DeploymentXMLUtil.constructDeployment(
                this.args.getDurationMinutes(), this.args.getMemory(), this.args.getCores(),
                this.newUnpropTargetURL);

        this.executeUtil.startOneWorkspace(this.workspaceFactoryURL, metadata, this.args.getMetadata_fileName(),
                deploymentRequest, this.args.getDeploymentRequest_fileName(), this.args.getSshfile(),
                this.args.getHistoryDirectory(), this.args.getPollMs(), this.args.isUseNotifications(),
                this.args.getFactoryID(), this.print);

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    void action_run_cluster() throws ExecutionProblem, ExitNow {

        if (!this.args.getActions().contains(AllArgs.ACTION_RUN_CLUSTER)) {
            return; // *** EARLY RETURN ***
        }

        final String sectionTitle = "ACTION: RUN CLUSTER";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        final int len = this.clusterMembers.length;

        final VirtualWorkspace_Type[] metadatas = new VirtualWorkspace_Type[len];
        final WorkspaceDeployment_Type[] deploymentRequests = new WorkspaceDeployment_Type[len];

        final String[] metadata_fileNames = new String[len];
        final String[] deploymentRequest_fileNames = new String[len];

        for (int i = 0; i < len; i++) {

            final ClusterMember member = this.clusterMembers[i];
            if (member == null) {
                throw new IllegalStateException("valid clusterMembers must be present here");
            }

            final String imageName = member.getImageName();

            final String imageURL;
            try {
                imageURL = this.repoUtil.getDerivedImageURL(imageName);
            } catch (Exception e) {
                throw new ExecutionProblem("Problem with image name '" + imageName + "': " + e.getMessage(), e);
            }

            final URI imageURI;
            try {
                imageURI = new URI(imageURL);
            } catch (URI.MalformedURIException e) {
                throw new ExecutionProblem(e.getMessage(), e);
            }

            // runName (3rd method paramater, being set to null) will be set
            // in startWorkspaceCluster once rundir is known
            metadatas[i] = MetadataXMLUtil.constructMetadata(imageURI, this.args.getMetadata_mountAs(), null,
                    member.getAssociations(), member.getIfaceNames(), this.args.getMetadata_cpuType(),
                    this.args.getMetadata_vmmVersion(), this.args.getMetadata_vmmType(), this.kernelURI);

            deploymentRequests[i] = DeploymentXMLUtil.constructDeployment(this.args.getDurationMinutes(),
                    this.args.getMemory(), this.args.getCores(), this.newUnpropTargetURL, member.getQuantity());

            metadata_fileNames[i] = HistoryUtil.getMemberName(i + 1) + "-" + this.args.getMetadata_fileName();

            deploymentRequest_fileNames[i] = HistoryUtil.getMemberName(i + 1) + "-"
                    + this.args.getDeploymentRequest_fileName();
        }

        ClusterUtil.printClusterInfo(this.clusterMembers, this.print);

        final String[] printNames = new String[this.clusterMembers.length];

        final Cloudcluster_Type[] clustersForUserData = new Cloudcluster_Type[this.clusterMembers.length];

        for (int i = 0; i < this.clusterMembers.length; i++) {
            final int memberIndex = i;
            final ClusterMember member = this.clusterMembers[memberIndex];

            printNames[i] = member.getPrintName(); // may be null

            // will be null if not involved in contextualization
            clustersForUserData[memberIndex] = member.getClusterForUserData();
            printNames[i] = member.getPrintName(); // may be null
        }

        final KnownHostsTask[] knownHostTasks;
        if (this.args.getSsh_hostsfile() != null) {
            knownHostTasks = ClusterUtil.constructKnownHostTasks(this.clusterMembers, this.args.isHostkeyDir());
        } else {
            knownHostTasks = null;
        }

        this.executeUtil.startWorkspaceCluster(this.workspaceFactoryURL, knownHostTasks, metadatas,
                metadata_fileNames, deploymentRequests, deploymentRequest_fileNames, printNames,
                clustersForUserData, !this.args.isNoContextLock(), this.args.getSshfile(),
                this.args.getSsh_hostsfile(), this.args.getHistoryDirectory(), this.args.getPollMs(),
                this.args.getFactoryID(), this.print, this.args.getBrokerURL(), this.args.getBrokerID());

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    class CtxMemberInfo {
        private final String image;
        private final int quantity;
        private final ClusterMember clusterMember;

        public CtxMemberInfo(ClusterMember member) throws ExecutionProblem {
            if (member == null) {
                throw new IllegalArgumentException("member may not be null");
            }

            this.clusterMember = member;

            this.image = member.getImageName();
            if (this.image == null || this.image.trim().length() == 0) {
                throw new ExecutionProblem("image name missing from " + "cluster document");
            }
            this.quantity = member.getQuantity();

        }
    }

    void validateClusterMembersForCtxClient() throws ExecutionProblem {
        if (this.clusterMembers == null) {
            throw new ExecutionProblem("no clusterMembers provided");
        }
        for (ClusterMember member : this.clusterMembers) {
            if (member == null) {
                throw new IllegalStateException("valid clusterMembers must be present here");
            }

            final String imageName = member.getImageName();
            if (imageName == null || imageName.trim().length() == 0) {
                throw new ExecutionProblem("No AMI in cluster file");
            }

        }

    }

    void action_init_context() throws ExecutionProblem, ExitNow {
        if (!this.args.getActions().contains(AllArgs.ACTION_INIT_CONTEXT)) {
            return; // *** EARLY RETURN ***
        }

        String brokerURL = this.args.getBrokerURL();
        String brokerIdentityAuthorization = this.args.getBrokerID();
        if (brokerURL == null) {
            int dx = this.workspaceFactoryURL.indexOf("WorkspaceFactoryService");
            brokerURL = this.workspaceFactoryURL.substring(0, dx);
            brokerURL += "NimbusContextBroker";
            print.debugln("No context broker URL was explicitly supplied,"
                    + " so it has been deduced from the nimbus factory URL." + " Using: " + brokerURL);

            // and so ID scheme for service must be copied
            if (brokerIdentityAuthorization == null && this.args.getFactoryID() != null) {
                brokerIdentityAuthorization = this.args.getFactoryID();
            }
        }

        this.validateClusterMembersForCtxClient();

        this.executeUtil.ctxClusterHelp(this.clusterMembers, brokerURL, brokerIdentityAuthorization,
                !this.args.isNoContextLock(), this.args.getInitCtxDir(), this.print);
    }

    void action_print_context_status() throws ExecutionProblem, ExitNow {
        if (!this.args.getActions().contains(AllArgs.ACTION_PRINT_CTX_STATUS)) {
            return; // *** EARLY RETURN ***
        }

        String brokerIdentityAuthorization = this.args.getBrokerID();
        if (brokerIdentityAuthorization == null && this.args.getFactoryID() != null) {
            brokerIdentityAuthorization = this.args.getFactoryID();
        }

        this.executeUtil.printContextStatusQuery(this.specificEPRpath, brokerIdentityAuthorization, this.print);
    }

    void action_run_ec2cluster() throws ExecutionProblem, ExitNow {

        if (!this.args.getActions().contains(AllArgs.ACTION_EC2_CLUSTER)) {
            return; // *** EARLY RETURN ***
        }

        this.validateClusterMembersForCtxClient();

        final String sectionTitle = "ACTION: EC2 CLUSTER HELP";
        CommonPrint.printDebugSection(this.print, sectionTitle);

        print.infoln("\nEC2 cluster:");
        for (int i = 0; i < this.clusterMembers.length; i++) {
            final ClusterMember member = this.clusterMembers[i];
            String inststr = " instance";
            if (member.getQuantity() > 1) {
                inststr += "s";
            }

            final String mname;
            if (member.getPrintName() == null) {
                mname = HistoryUtil.getMemberName(i + 1);
            } else {
                mname = member.getPrintName();
            }

            this.print.infoln(
                    "  - " + mname + ": AMI '" + member.getImageName() + "', " + member.getQuantity() + inststr);
        }

        String brokerURL = this.args.getBrokerURL();
        String brokerIdentityAuthorization = this.args.getBrokerID();
        if (brokerURL == null) {
            int dx = this.workspaceFactoryURL.indexOf("WorkspaceFactoryService");
            brokerURL = this.workspaceFactoryURL.substring(0, dx);
            brokerURL += "NimbusContextBroker";
            print.debugln("No context broker URL was explicitly supplied,"
                    + " so it has been deduced from the nimbus factory URL." + " Using: " + brokerURL);

            // and so ID scheme for service must be copied
            if (brokerIdentityAuthorization == null && this.args.getFactoryID() != null) {
                brokerIdentityAuthorization = this.args.getFactoryID();
            }
        }

        this.executeUtil.ec2ClusterHelp(this.clusterMembers, brokerURL, brokerIdentityAuthorization,
                !this.args.isNoContextLock(), this.args.getPollMs(), this.args.getEc2ScriptPath(),
                this.args.getHistoryDirectory(), this.print);

        CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
    }

    void action_download() throws ExecutionProblem {
        if (this.args.getActions().contains(AllArgs.ACTION_DOWNLOAD)) {
            final String sectionTitle = "ACTION: DOWNLOAD";
            CommonPrint.printDebugSection(this.print, sectionTitle);

            final String localfile = this.args.getLocalfile();
            final String name = this.args.getName();

            this.repoUtil.downloadVM(localfile, name, this.print.getInfoProxy(), this.print.getDebugProxy(),
                    this.executeUtil.getExecer());

            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }

    void action_delete() throws ExecutionProblem {
        if (this.args.getActions().contains(AllArgs.ACTION_DELETE)) {
            final String sectionTitle = "ACTION: DELETE";
            CommonPrint.printDebugSection(this.print, sectionTitle);

            this.repoUtil.deleteVM(this.args.getName(), this.print.getInfoProxy(), this.print.getDebugProxy());

            CommonPrint.printDebugSectionEnd(this.print, sectionTitle);
        }
    }
}