# ProcessesPresenter.java

    /*
    ** Module   : ProcessesPresenter.java
    ** Abstract : ProcessesPresenter implements business methods to manage process accounts.
    **
    ** Copyright (c) 2017, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ----------------Description----------------------
    ** 001 SBI 20170601 Created initial version.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.admin.client.application.home.accounts.processes;

    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.logging.Level;
    import java.util.Set;

    import javax.inject.Named;

    import com.goldencode.p2j.admin.*;
    import com.goldencode.p2j.admin.client.AdminCallback;
    import com.goldencode.p2j.admin.client.MultiCallback;
    import com.goldencode.p2j.admin.client.NameTokens;
    import com.goldencode.p2j.admin.client.application.Alarm;
    import com.goldencode.p2j.admin.client.application.PrintingContext;
    import com.goldencode.p2j.admin.client.application.home.HomePresenter;
    import com.goldencode.p2j.admin.client.application.home.ViewStateMachine;
    import com.goldencode.p2j.admin.client.application.home.accounts.AccountsPresenter;
    import com.goldencode.p2j.admin.client.application.home.acl.*;
    import com.goldencode.p2j.admin.client.widget.dialog.MessageType;
    import com.goldencode.p2j.admin.client.widget.dialog.ModalDialogs;
    import com.goldencode.p2j.admin.shared.AdminServiceAsync;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.*;
    import com.google.inject.Inject;
    import com.google.inject.Provider;
    import com.google.web.bindery.event.shared.EventBus;
    import com.gwtplatform.mvp.client.HasUiHandlers;
    import com.gwtplatform.mvp.client.View;
    import com.gwtplatform.mvp.client.annotations.NameToken;
    import com.gwtplatform.mvp.client.annotations.ProxyStandard;
    import com.gwtplatform.mvp.client.presenter.slots.NestedSlot;
    import com.gwtplatform.mvp.client.proxy.PlaceManager;
    import com.gwtplatform.mvp.client.proxy.ProxyPlace;
    import com.gwtplatform.mvp.shared.proxy.PlaceRequest;

    /**
     * ProcessesPresenter implements business methods to manage process accounts.
     */
    public class ProcessesPresenter
    extends AccountsPresenter<ProcessesPresenter.MyView, ProcessesPresenter.MyProxy>
    implements ProcessesUIHandlers
    {

       /** Defines the initial empty state */
       private static final boolean[] STATE_INITIAL_EMPTY =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** Defines the add new process account state */
       private static final boolean[] STATE_ADD_PROCESS =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** Defines the edit existing process account state */
       private static final boolean[] STATE_EDIT_PROCESS =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** Defines the cloning process account state */
       private static final boolean[] STATE_CLONE_PROCESS =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** state definition - single selection in process accounts list */
       private static final boolean[] STATE_PROCS_SINGLE =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ true,
          /* cloneProcess         */ true,
          /* editProcess          */ true,
          /* disableProcess       */ true,
          /* switchToACLs         */ true,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** state definition - single selection in certificate list */
       private static final boolean[] STATE_CERTS_SINGLE =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ true,
          /* switchToCerts        */ true
       };

       /** Defines the state in which the assign certificate action is disabled. */
       private static final boolean[] STATE_CERTS_ASSIGN_DISABLED =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ true
       };

       /** state definition - multiple selection in process accounts list */
       private static final boolean[] STATE_PROCS_MULTI =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ true,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** mask definition - west */
       private static final boolean[] MASK_WEST =
       {
          /* refreshProcesses     */ true,
          /* addProcess           */ true,
          /* deleteProcessAccounts*/ true,
          /* cloneProcess         */ true,
          /* editProcess          */ true,
          /* disableProcess       */ true,
          /* switchToACLs         */ true,
          /* refreshCerts         */ false,
          /* assignCert           */ false,
          /* switchToCerts        */ false
       };

       /** mask definition - east */
       private static final boolean[] MASK_EAST =
       {
          /* refreshProcesses     */ false,
          /* addProcess           */ false,
          /* deleteProcessAccounts*/ false,
          /* cloneProcess         */ false,
          /* editProcess          */ false,
          /* disableProcess       */ false,
          /* switchToACLs         */ false,
          /* refreshCerts         */ true,
          /* assignCert           */ true,
          /* switchToCerts        */ true
       };

       /**
        * Defines the Process Accounts View states
        */
       public static enum ProcessesViewStates
       implements ViewStateMachine.TransitionState
       {
          /** The processes list has been refreshed currently */
          PROCESSES_LIST_REFRESHED(MASK_WEST, STATE_INITIAL_EMPTY),

          /** The process account has been added currently */
          ADD_PROCESS_ACCOUNT(MASK_WEST, STATE_ADD_PROCESS),

          /** The process account has been edited currently */
          EDIT_PROCESS_ACCOUNT(MASK_WEST, STATE_EDIT_PROCESS),

          /** The process account has been cloned currently */
          CLONE_PROCESS_ACCOUNT(MASK_WEST, STATE_CLONE_PROCESS),

          /** The single account has been selected currently from the all processes table */
          SINGLE_PROCESS_SELECTED(MASK_WEST, STATE_PROCS_SINGLE),

          /** The multiple accounts have been selected currently from the all processes table */
          MULTI_PROCESSES_SELECTED(MASK_WEST, STATE_PROCS_MULTI),

          /** The certificate has been selected currently from the peer certificates list */
          CERT_SELECTED(MASK_EAST, STATE_CERTS_SINGLE),

          /** The action to assign certificate has been disabled currently */
          CERT_ASSIGN_DISABLED(MASK_EAST, STATE_CERTS_ASSIGN_DISABLED);

          /** The current components states to be enabled or disabled */
          private final boolean[] state;

          /** The current mask that defines components to which the current state is applied */
          private final boolean[] mask;

          /**
           * Creates a target view state.
           *
           * @param    mask
           *           The current mask
           * @param    state
           *           The current state
           */
          private ProcessesViewStates(boolean[] mask, boolean[] state)
          {
             this.mask  = mask;
             this.state = state;
          }

          /**
           * Gets the current state that defines the affected components states.
           *
           * @return   The current state
           */
          @Override
          public boolean[] getState()
          {
             return this.state;
          }

          /**
           * Gets the current mask that defines the set of affected components (widgets).
           *
           * @return   The current mask
           */
          @Override
          public boolean[] getMask()
          {
             return mask;
          }
       }

       /**
        * This view interface that must be implemented by the concrete view according to GWTP
        * framework. Defines the exposed business methods.
        */
       public interface MyView
       extends View, HasUiHandlers<ProcessesUIHandlers>, Provider<HasEnabled[]>, PrintingContext
       {
          /**
           * Sets the all process accounts table model.
           *
           * @param    processes
           *           The array of process accounts given by tagged names
           */
          void setProcesses(TaggedName[] processes);

          /**
           * Sets the peer certificates table model.
           *
           * @param    certs
           *           The array of peer certificates given by tagged names
           */
          void setCerts(TaggedName[] certs);

          /**
           * Tests if the given process account is selected now.
           *
           * @param    account
           *           The given process account
           *
           * @return   True iff this process account is selected now
           */
          boolean isProcessAccountSelected(TaggedName account);

          /**
           * Tests if the given set of process accounts is selected now.
           *
           * @param    accounts
           *           The given set of process accounts
           *
           * @return   True iff this given set of process accounts is selected now
           */
          boolean isProcessAccountsSelected(Set<TaggedName> accounts);

          /**
           * Selects the target process account by its tagged name.
           *
           * @param    account
           *           The tagged name
           */
          void selectProcessAccount(TaggedName account);

          /**
           * Selects the target process account by its account name.
           *
           * @param    name
           *           The account name
           */
          void selectProcessAccountByName(String name);

          /**
           * Assigns the given peer certificate to the selected process account.
           *
           * @param    selectedAccount
           *           The selected process account
           * @param    alias
           *           The peer certificate alias name
           */
          void setAssignedCert(TaggedName selectedAccount, String alias);

          /**
           * Toggles the label of the Disable/Enable process account button.
           *
           * @param    selectedAccount
           *           The selected process account
           * @param    enabled
           *           The enabled flag which true value indicates that the target buttons label
           *           becomes "Disable Process", otherwise this label becomes "Enable Process".
           */
          void toggleDisableProcessButton(TaggedName selectedAccount, boolean enabled);

          /**
           * Tests if the given peer certificate is selected.
           *
           * @param    cert
           *           The given peer certificate
           *
           * @return   True iff the given peer certificate is selected, otherwise false.
           */
          boolean isCertificateSelected(TaggedName cert);

          /**
           * Gets a reference to the Clone Process Account View.
           *
           * @return   The Clone Group View
           */
          CloneProcessView getCloneProcessView();

          /**
           * Returns the parent container where the Add/Edit Process Account Definition View will be
           * revealed in.
           *
           * @return   The parent container for the Add/Edit Process Account Definition View.
           */
          HasWidgets getProcDefModalParent();
       }

       /**
        * Defines the proxy place for GWTP framework.
        */
       @ProxyStandard
       @NameToken(NameTokens.PROCESSES)
       public interface MyProxy
       extends ProxyPlace<ProcessesPresenter>
       {}

       /** The attachment point for modal dialogs on this view*/
       public static final NestedSlot MODAL_CONTENT = new NestedSlot();

       /** Administration server interface */
       private final AdminServiceAsync adminService;

       /** The common modal dialogs manager */
       private final ModalDialogs modalDialogsManager;

       /** The server alarms manager */
       private final Alarm serverAlarmsManager;

       /** The current selected set of process accounts */
       private final Set<ProcessDef> selectedProcesses = new HashSet<ProcessDef>();

       /** The current single selected process account */
       private ProcessDef selectedProcessAccount;

       /** The process account definition provider */
       private final Provider<ProcessAccountDefinition> padProvider;

       /**
        * Constructs this presenter(controller) used by MPV gwtplatform of ArcBees Inc.
        *
        * @param    eventBus
        *           The event bus
        * @param    view
        *           The associated view
        * @param    proxy
        *           The proxy place
        * @param    placeManager
        *           The place manager
        * @param    viewStateMachine
        *           The view state machine
        * @param    adminService
        *           The administration service
        * @param    modalDialogsManager
        *           The common modal dialogs manager
        * @param    serverAlarmsManager
        *           The server alarms
        * @param    padProvider
        *           The process account definition provider
        * @param    aclPopupPresenterProvider
        *           The ACL popup presenter provider
        */
       @Inject
       public ProcessesPresenter(EventBus eventBus,
                                 MyView view,
                                 MyProxy proxy,
                                 PlaceManager placeManager,
                                 @Named("ProcessesViewStateMachine")
                                 ViewStateMachine viewStateMachine,
                                 AdminServiceAsync adminService,
                                 ModalDialogs modalDialogsManager,
                                 Alarm serverAlarmsManager,
                                 Provider<ProcessAccountDefinition> padProvider,
                                 ACLPopupPresenter aclPopupPresenterProvider)
       {
          super(eventBus,
                view,
                proxy,
                HomePresenter.SLOT_CONTENT,
                placeManager,
                viewStateMachine,
                aclPopupPresenterProvider);

          this.adminService = adminService;
          this.modalDialogsManager = modalDialogsManager;
          this.serverAlarmsManager = serverAlarmsManager;
          this.padProvider = padProvider;
          getView().setUiHandlers(this);
       }

       /**
        * Loads the data before the associated view will be displayed.
        *
        * @param    request
        *           The current request
        */
       @Override
       public void prepareFromRequest(PlaceRequest request)
       {
          super.prepareFromRequest(request);
          updateProcesses();
          updateCerts();
          setState(ProcessesViewStates.PROCESSES_LIST_REFRESHED);
       }

       /**
        * Handles the action to refresh the table of process accounts.
        */
       @Override
       public void onRefreshProcesses()
       {
          updateProcesses(()->{setState(ProcessesViewStates.PROCESSES_LIST_REFRESHED);});
       }

       /**
        * Handles the action to refresh the table of certificates.
        */
       @Override
       public void onRefreshCerts()
       {
          updateCerts();
       }

       /**
        * Updates the process accounts data model asynchronously.
        */
       private void updateProcesses()
       {
          updateProcesses(()->{});
       }

       /**
        * Updates the process accounts data model asynchronously and executes the given command if
        * this action has been complete successfully or has been failed.
        *
        * @param    command
        *           The given command
        */
       private void updateProcesses(Runnable command)
       {
          this.adminService.listProcesses(new AsyncCallback<TaggedName[]>()
          {

             @Override
             public void onSuccess(TaggedName[] processes)
             {
                getView().setProcesses(processes);
                command.run();
             }

             @Override
             public void onFailure(Throwable caught)
             {
                command.run();
                serverAlarmsManager.ring("Failed to get processes");
             }
          });
       }

       /**
        * Updates the peer certificates data model asynchronously.
        */
       private void updateCerts()
       {
          updateCerts(()->{});
       }

       /**
        * Updates the peer certificates data model asynchronously and executes the given command if
        * this action has been complete successfully or has been failed.
        *
        * @param    command
        *           The given command
        */
       private void updateCerts(Runnable command)
       {
          this.adminService.listPeerCerts(false, new AsyncCallback<TaggedName[]>()
          {

             @Override
             public void onSuccess(TaggedName[] certs)
             {
                getView().setCerts(certs);
                command.run();
             }

             @Override
             public void onFailure(Throwable caught)
             {
                command.run();
                serverAlarmsManager.ring("Failed to get peer certificates");
             }
          });
       }

       /**
        * Cancels the Add Process Account Dialog.
        */
       @Override
       public void cancelAddProcessAccountDialog()
       {
          restoreState();
       }

       /**
        * Cancels the Edit Process Account Dialog.
        */
       @Override
       public void cancelEditProcessAccountDialog()
       {
          restoreState();
       }

       /**
        * Shows the Add Process Account Dialog.
        */
       @Override
       public void showAddProcessAccountDialog()
       {
          ProcessAccountDefinition pad = padProvider.get();
          pad.build(null, r ->
          {
             if (!r)
             {
                return;
             }

             pad.show(getView().getProcDefModalParent(), new ProcessDef(), pdata ->
             {
                if (pdata == null)
                {
                   return;
                }

                addProcess(pdata.processDef, pdata.extDef);
                updateProcesses();
             });
          });
       }

       /**
        * Shows the Edit Process Account Dialog.
        *
        * @param    selected
        *           The process account given by its tagged name
        */
       @Override
       public void showEditProcessAccountDialog(TaggedName selected)
       {
          ProcessAccountDefinition pad = padProvider.get();
          pad.build(selectedProcessAccount, r ->
          {
             if (!r)
             {
                return;
             }

             pad.show(getView().getProcDefModalParent(), selectedProcessAccount, pdata ->
             {
                if (pdata == null)
                {
                   return;
                }

                updateProcess(pdata.processDef, pdata.extDef);
                updateProcesses();
             });
          });
       }

       /**
        * Setups the Clone Process Account Dialog.
        *
        * @param    selected
        *           The process account given by its tagged name
        */
       @Override
       public void setupCloneProcessAccountDialog(TaggedName selected)
       {
          getView().getCloneProcessView().setupClonedProcess(selected);
       }

       /**
        * Handles the process accounts selection changed action.
        *
        * @param    selected
        *           The set of selected process accounts given by tagged names
        */
       @Override
       public void onProcessSelectionChanged(Set<TaggedName> selected)
       {
          if (selected == null || selected.isEmpty())
          {
             return;
          }

          MultiCallback mc = new MultiCallback(adminService, serverAlarmsManager);
          LinkedHashMap<TaggedName, AdminCallback<ProcessDef>> callbacks = new LinkedHashMap<TaggedName, AdminCallback<ProcessDef>>();
          selected.forEach(ps -> {
             callbacks.put(ps, mc.newCallback());
             logger.info("get process definition for " + ps.getName());
          });
          for(Entry<TaggedName, AdminCallback<ProcessDef>> e : callbacks.entrySet())
          {
             adminService.getProcess(e.getKey().getName(), e.getValue());
          }

          mc.onDone(() -> {
             selectedProcesses.clear();
             selectedProcessAccount = null;
             if (mc.isSuccess())
             {
                for(Entry<TaggedName, AdminCallback<ProcessDef>> e : callbacks.entrySet())
                {
                   ProcessDef ps = e.getValue().getResult();
                   selectedProcesses.add(ps);
                }
                if (selectedProcesses.size() > 1)
                {
                   updateCerts(() -> {
                      setState(ProcessesViewStates.MULTI_PROCESSES_SELECTED);
                   });
                }
                else if (selectedProcesses.size() == 1)
                {
                   selectedProcessAccount = selectedProcesses.toArray(new ProcessDef[1])[0];
                   String alias = selectedProcessAccount.alias;
                   boolean enabledAccount = selectedProcessAccount.enabled;
                   TaggedName selectedAccount = callbacks.keySet().toArray(new TaggedName [1])[0];
                   setState(ProcessesViewStates.SINGLE_PROCESS_SELECTED);
                   getView().toggleDisableProcessButton(selectedAccount, enabledAccount);
                   getView().setAssignedCert(selectedAccount, alias);
                }
                else
                {
                   updateCerts(() -> {
                      setState(ProcessesViewStates.PROCESSES_LIST_REFRESHED);
                   });
                }
             }
             else
             {
                for(Entry<TaggedName, AdminCallback<ProcessDef>> e : callbacks.entrySet())
                {
                   if (e.getValue().getResult() == null)
                   {
                      logger.info("failed to get process definition for " + e.getKey().getName());
                   }
                }
                updateCerts(() -> {
                   setState(ProcessesViewStates.PROCESSES_LIST_REFRESHED);
                });
             }
          });

       }

       /**
        * Cancels the Clone Process Account Dialog.
        */
       @Override
       public void cancelCloneProcessAccountDialog()
       {
       }

       /**
        * Adds the new process account.
        *
        * @param    accountDef
        *           The process account definition
        * @param    ext
        *           The account extension object
        */
       @Override
       public void addProcess(ProcessDef accountDef, Serializable ext)
       {
          adminService.addProcess(accountDef, ext, new AsyncCallback<Boolean>()
          {
             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "Couldn't add process account " + accountDef.subjectId, caught);
                updateProcesses();
                restoreState();
             }

             @Override
             public void onSuccess(Boolean result)
             {
                updateProcesses();
                if (result == null || !result)
                {
                   serverAlarmsManager.ring("Couldn't add process account " + accountDef.subjectId);
                }
                restoreState();
             }
          });
       }

       /**
        * Updates the given process account.
        *
        * @param    accountDef
        *           The given process account definition
        * @param    ext
        *           The given account extension object
        */
       @Override
       public void updateProcess(ProcessDef accountDef, Serializable ext)
       {
          adminService.setProcess(accountDef, ext, new AsyncCallback<Boolean>()
          {
             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "Couldn't update process account " + accountDef.subjectId, caught);
                updateProcesses(()->{getView().selectProcessAccountByName(accountDef.subjectId);});
                restoreState();
             }

             @Override
             public void onSuccess(Boolean result)
             {
                updateProcesses(()->{getView().selectProcessAccountByName(accountDef.subjectId);});
                if (result == null || !result)
                {
                   serverAlarmsManager.ring("Couldn't update process account " + accountDef.subjectId);
                }
                restoreState();
             }
          });
       }

       /**
        * Executes the action to disable the given process account.
        *
        * @param    ps
        *           The process account given by its tagged name
        */
       @Override
       public void disableProcessAccount(TaggedName ps)
       {
          if (selectedProcessAccount == null || selectedProcessAccount.subjectId == null ||
                   !selectedProcessAccount.subjectId.equals(ps.getName()))
          {
             return;
          }
          boolean enable = !selectedProcessAccount.enabled;
          String title   = enable ? "Enable Process" : "Disable Process";
          String message = (enable ? "Enable Process '" : "Disable Process '") + ps.getName() + "'?";
          modalDialogsManager.showYesNo(title, message, MessageType.QUESTION, button -> {

             if (button == 0)
             {
                selectedProcessAccount.enabled = enable;
                adminService.setProcess(selectedProcessAccount, new AsyncCallback<Boolean>()
                {

                   @Override
                   public void onFailure(Throwable caught)
                   {
                      updateProcesses(() -> {getView().selectProcessAccount(ps);});
                      logger.log(Level.SEVERE, "setProcess " + ps.getName() + " got " + caught);
                      modalDialogsManager.showError("Can't " + (enable ? "enable" : "disable") +
                               ps.getName() + " process account.");
                   }

                   @Override
                   public void onSuccess(Boolean result)
                   {
                      updateProcesses(() -> {getView().selectProcessAccount(ps);});
                      if (!result)
                      {
                         serverAlarmsManager.ring("Can't update to " +
                                  (enable ? "enable" : "disable") + ps.getName() + " process account.");
                      }
                   }
                });
             }
          });
       }

       /**
        * Clones the process account with its ACL if it is chosen.
        *
        * @param    nameFrom
        *           The cloning process account name
        * @param    nameTo
        *           The new cloned process account name
        * @param    acls
        *           The flag that requests to clone the process account ACL
        */
       @Override
       public void cloneProcess(String nameFrom, String nameTo, boolean acls)
       {
          adminService.cloneProcess(nameFrom, nameTo, acls, new AsyncCallback<Boolean>()
          {

             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "cloneProcess('" + nameFrom + "', '" + nameTo + "', '" + acls + "') got " + caught);
                onRefreshProcesses();
             }

             @Override
             public void onSuccess(Boolean result)
             {
                if (!result)
                {
                   serverAlarmsManager.ring("Can't clone process " + nameFrom);
                }
                onRefreshProcesses();
             }
          });
       }

       /**
        * Executes the action to delete the given process accounts
        *
        * @param    accounts
        *           The process accounts to be deleted
        */
       @Override
       public void deleteProcessAccounts(Set<TaggedName> accounts)
       {
          if (accounts == null || accounts.isEmpty())
          {
             return;
          }
          int size = accounts.size();
          String message;
          if (size > 1)
          {
             message = size + " process accounts being deleted. Continue?";
          }
          else
          {
             message = "1 process account being deleted. Continue?";
          }
          String title   = "Deleting Process Accounts";
          modalDialogsManager.showYesNo(title, message, MessageType.QUESTION, button -> {

             if (button == 0)
             {
                MultiCallback mc = new MultiCallback(adminService, serverAlarmsManager);
                LinkedHashMap<TaggedName, AdminCallback<Boolean>> callbacks = new LinkedHashMap<TaggedName, AdminCallback<Boolean>>();
                for(TaggedName ps : accounts)
                {
                   AdminCallback<Boolean> callback = mc.newCallback();
                   callbacks.put(ps, callback);
                   logger.info("process account to delete " + ps.getName());
                };
                for (Map.Entry<TaggedName, AdminCallback<Boolean>> e : callbacks.entrySet())
                {
                   this.adminService.deleteProcess(e.getKey().getName(), e.getValue());
                }
                mc.onDone(() ->
                {
                   onRefreshProcesses();
                   if (!mc.isSuccess())
                   {
                      for (Map.Entry<TaggedName, AdminCallback<Boolean>> e : callbacks.entrySet())
                      {
                         if (e.getValue().getResult() == null || !e.getValue().getResult())
                         {
                            logger.info("failed to delete process account " + e.getKey().getName());
                         }
                      }
                   }
                });
             }
          });
       }

       /**
        * Handles the certificate selection changed action.
        *
        * @param    selectedCert
        *           The selected certificate
        */
       @Override
       public void onCertificateSelectionChanged(TaggedName selectedCert)
       {
          if (!getView().isCertificateSelected(selectedCert))
          {
             return;
          }

          if (selectedProcessAccount != null)
          {
             String alias = selectedCert.getName();
             boolean alreadyAssigned = (selectedProcessAccount.alias != null &&
                      selectedProcessAccount.alias.equals(alias));
             if (!alreadyAssigned)
             {
                changeState(ProcessesViewStates.CERT_SELECTED);
             }
             else
             {
                changeState(ProcessesViewStates.CERT_ASSIGN_DISABLED);
             }
          }
          else
          {
             changeState(ProcessesViewStates.CERT_ASSIGN_DISABLED);
          }
       }

       /**
        * Executes the action to assign the selected certificate to the selected process account.
        *
        * @param    selectedCert
        *           The selected certificate given by its tagged name
        * @param    selectedProcess
        *           The selected process account given by its tagged name
        */
       @Override
       public void assignCertificateToProcess(TaggedName selectedCert, TaggedName selectedProcess)
       {
          if (!getView().isCertificateSelected(selectedCert) ||
                   !getView().isProcessAccountSelected(selectedProcess))
          {
             return;
          }
          String title   = "Assign Certificate";
          String message = "Assign certificate " + "'" + selectedCert.getName() + "'" + " to process " +
                   "'" + selectedProcess.getName() + "'" + "?";
          modalDialogsManager.showYesNo(title, message, MessageType.QUESTION, (button) -> {
             if (button == 0)
             {
                adminService.getProcess(selectedProcess.getName(), new AsyncCallback<ProcessDef>()
                {

                   @Override
                   public void onFailure(Throwable caught)
                   {
                      logger.log(Level.SEVERE, "Failed to call getProcess", caught);
                   }

                   @Override
                   public void onSuccess(ProcessDef result)
                   {
                      result.alias = selectedCert.getName();
                      updateProcess(result, null);
                   }
                });
             }
          });
       }

       /**
        * Executes the action to switch to the Certificate Management view.
        *
        * @param    selected
        *           The certificate given by its tagged name
        */
       @Override
       public void switchToCertificates(TaggedName selected)
       {
          PlaceRequest request = new PlaceRequest.Builder().nameToken(NameTokens.CERTS).with("alias",
                   selected.getName()).with("CertificatesOptions", "PEER").build();
          getPlaceManager().revealPlace(request);
       }
    }