package org.ovirt.engine.ui.frontend; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.aaa.DbUser; import org.ovirt.engine.core.common.errors.EngineFault; import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.ui.frontend.IFrontendEventsHandler.MessageFormatter; import org.ovirt.engine.ui.frontend.communication.AsyncOperationCompleteEvent; import org.ovirt.engine.ui.frontend.communication.AsyncOperationStartedEvent; import org.ovirt.engine.ui.frontend.communication.RefreshActiveModelEvent; import org.ovirt.engine.ui.frontend.communication.StorageCallback; import org.ovirt.engine.ui.frontend.communication.UserCallback; import org.ovirt.engine.ui.frontend.communication.VdcOperation; import org.ovirt.engine.ui.frontend.communication.VdcOperationCallback; import org.ovirt.engine.ui.frontend.communication.VdcOperationCallbackList; import org.ovirt.engine.ui.frontend.communication.VdcOperationManager; import org.ovirt.engine.ui.uicompat.ConstantsManager; import org.ovirt.engine.ui.uicompat.Event; import org.ovirt.engine.ui.uicompat.EventArgs; import org.ovirt.engine.ui.uicompat.FrontendActionAsyncResult; import org.ovirt.engine.ui.uicompat.FrontendMultipleActionAsyncResult; import org.ovirt.engine.ui.uicompat.FrontendMultipleQueryAsyncResult; import org.ovirt.engine.ui.uicompat.IFrontendActionAsyncCallback; import org.ovirt.engine.ui.uicompat.IFrontendMultipleActionAsyncCallback; import org.ovirt.engine.ui.uicompat.IFrontendMultipleQueryAsyncCallback; import org.ovirt.engine.ui.uicompat.UIConstants; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.HasHandlers; import com.google.gwt.user.client.rpc.StatusCodeException; import com.google.inject.Inject; /** * The {@code Frontend} class is the interface between the front-end and the back-end. It manages the operations that * are being sent to the back-end and returns the results back to the callers. This class is designed as a singleton * class. * <p> * Legacy code or code not managed within application's GIN context can use {@link #getInstance()} to retrieve the * instance of this class. */ public class Frontend implements HasHandlers { /** * Provides static access to {@code Frontend} singleton instance. */ public static class InstanceHolder { @Inject static Frontend instance; } /** * Empty implementation of IFrontendActionAsyncCallback. */ private static final class NullableFrontendActionAsyncCallback implements IFrontendActionAsyncCallback { @Override public void executed(final FrontendActionAsyncResult result) { // Do nothing, we are the 'NullableFrontendActionAsyncCallback' } } /** * Empty callback. Use this when we don't want anything called when an operation completes. */ private static final IFrontendActionAsyncCallback NULLABLE_ASYNC_CALLBACK = new NullableFrontendActionAsyncCallback(); /** * The logger. */ private static final Logger logger = Logger.getLogger(Frontend.class.getName()); /** * The {@code VdcOperationManager}. */ private final VdcOperationManager operationManager; /** * Action error translator. */ private final ErrorTranslator validateErrorsTranslator; /** * VDSM error translator. */ private final ErrorTranslator vdsmErrorsTranslator; /** * The events handler. */ private IFrontendEventsHandler eventsHandler; /** * The {@code frontendFailureEvent} event. */ Event<FrontendFailureEventArgs> frontendFailureEvent = new Event<>("FrontendFailure", Frontend.class); //$NON-NLS-1$ /** * The {@code frontendNotLoggedInEvent} event. */ Event<EventArgs> frontendNotLoggedInEvent = new Event<>("NotLoggedIn", Frontend.class); //$NON-NLS-1$ /** * The currently logged in user. */ private DbUser currentUser; /** * Should queries be filtered. */ private boolean filterQueries; /** * UI constants, messages to show the user in the UI. */ private UIConstants constants; /** * GWT scheduler. */ private Scheduler scheduler; /** * GWT event bus. */ private final EventBus eventBus; /** * Constructor. * @param operationManager The {@code VdcOperationManger} to associate with this object. * @param applicationErrors The application error messages, we can use to translate application errors. * @param vdsmErrors The VDSM error messages, we can use to translate VDSM errors. * @param gwtEventBut The GWT event bus. */ @Inject public Frontend(final VdcOperationManager operationManager, final AppErrors applicationErrors, final VdsmErrors vdsmErrors, final EventBus gwtEventBus) { this(operationManager, new ErrorTranslator(applicationErrors), new ErrorTranslator(vdsmErrors), gwtEventBus); } /** * Constructor for unit testing. */ Frontend(final VdcOperationManager operationManager, final ErrorTranslator validateErrorsTranslator, final ErrorTranslator vdsmErrorsTranslator, final EventBus gwtEventBus) { this.operationManager = operationManager; this.validateErrorsTranslator = validateErrorsTranslator; this.vdsmErrorsTranslator = vdsmErrorsTranslator; eventBus = gwtEventBus; eventBus.addHandler(AsyncOperationCompleteEvent.getType(), event -> { if (event.isAction() && event.isSuccess()) { RefreshActiveModelEvent.fire(Frontend.this, true); } }); } /** * Gets the {@code VdcOperationManager} associated with this {@code Frontend}. * @return The operation manager. */ public VdcOperationManager getOperationManager() { return operationManager; } /** * Get an instance of the {@code Frontend} class. This is here to support legacy code, the appropriate way to * get an instance is have it injected by the IoC framework. * @return {@code Frontend} instance. */ public static Frontend getInstance() { return InstanceHolder.instance; } public static void setInstance(Frontend frontend) { InstanceHolder.instance = frontend; } /** * Run a non-public query against the back-end. * * @param queryType The type of the query. * @param parameters The parameters of the query. * @param callback The callback to call when the query completes. */ public void runQuery(final VdcQueryType queryType, final VdcQueryParametersBase parameters, final AsyncQuery callback) { runQuery(queryType, parameters, callback, false); } /** * Run a query against the back-end. * * @param queryType The type of the query. * @param parameters The parameters of the query. * @param callback The callback to call when the query completes. * @param isPublic Determine if the query is public or not. */ public void runQuery(final VdcQueryType queryType, final VdcQueryParametersBase parameters, final AsyncQuery callback, final boolean isPublic) { initQueryParamsFilter(parameters); final VdcOperation<VdcQueryType, VdcQueryParametersBase> operation = new VdcOperation<>(queryType, parameters, isPublic, false, new VdcOperationCallback<VdcOperation<VdcQueryType, VdcQueryParametersBase>, VdcQueryReturnValue>() { @Override public void onSuccess(final VdcOperation<VdcQueryType, VdcQueryParametersBase> operation, final VdcQueryReturnValue result) { try { if (!result.getSucceeded()) { // translate error enums to text result.setExceptionMessage(getAppErrorsTranslator().translateErrorTextSingle(result.getExceptionString())); logger.log(Level.WARNING, "Failure while invoking runQuery [" + result.getExceptionString() + ", " + result.getExceptionMessage() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (getEventsHandler() != null) { ArrayList<VdcQueryReturnValue> failedResult = new ArrayList<>(); failedResult.add(result); handleNotLoggedInEvent(result.getExceptionString()); } if (callback.isHandleFailure()) { callback.getAsyncCallback().onSuccess(result); } } else { if (callback.getConverter() != null) { callback.getAsyncCallback().onSuccess( callback.getConverter().convert(result.getReturnValue())); } else { callback.getAsyncCallback().onSuccess(result); } } } finally { fireAsyncQuerySucceededEvent(callback.getModel()); } } @Override public void onFailure(final VdcOperation<VdcQueryType, VdcQueryParametersBase> operation, final Throwable caught) { try { if (ignoreFailure(caught)) { return; } logger.log(Level.SEVERE, "Failed to execute runQuery: " + caught, caught); //$NON-NLS-1$ getEventsHandler().runQueryFailed(null); failureEventHandler(caught); if (callback.isHandleFailure()) { callback.getAsyncCallback().onSuccess(null); } } finally { fireAsyncQueryFailedEvent(callback.getModel()); } } }); // raise the query started event. fireAsyncOperationStartedEvent(callback.getModel()); getOperationManager().addOperation(operation); } /** * Run a query that does not require the user to be logged in. * @param queryType The type of query. * @param parameters The parameter of the query. * @param callback The callback to when the query completes. */ public void runPublicQuery(final VdcQueryType queryType, final VdcQueryParametersBase parameters, final AsyncQuery callback) { runQuery(queryType, parameters, callback, true); } /** * Run multiple queries in a single request to the back-end. * @param queryTypeList A list of {@code VdcQueryType}s. * @param queryParamsList A list of parameters associated with each query. * @param callback The callback to call when the query completes. */ public void runMultipleQueries(final List<VdcQueryType> queryTypeList, final List<VdcQueryParametersBase> queryParamsList, final IFrontendMultipleQueryAsyncCallback callback) { runMultipleQueries(queryTypeList, queryParamsList, callback, null); } /** * Run multiple queries in a single request to the back-end. * @param queryTypeList A list of {@code VdcQueryType}s. * @param queryParamsList A list of parameters associated with each query. * @param callback The callback to call when the query completes. * @param state The state object. */ public void runMultipleQueries(final List<VdcQueryType> queryTypeList, final List<VdcQueryParametersBase> queryParamsList, final IFrontendMultipleQueryAsyncCallback callback, final Object state) { VdcOperationCallbackList<VdcOperation<VdcQueryType, VdcQueryParametersBase>, List<VdcQueryReturnValue>> multiCallback = new VdcOperationCallbackList<VdcOperation<VdcQueryType, VdcQueryParametersBase>, List<VdcQueryReturnValue>>() { @Override public void onSuccess(final List<VdcOperation<VdcQueryType, VdcQueryParametersBase>> operationList, final List<VdcQueryReturnValue> resultObject) { logger.finer("Succesful returned result from runMultipleQueries!"); //$NON-NLS-1$ FrontendMultipleQueryAsyncResult f = new FrontendMultipleQueryAsyncResult(queryTypeList, queryParamsList, resultObject); callback.executed(f); fireAsyncQuerySucceededEvent(state); } @Override public void onFailure(final List<VdcOperation<VdcQueryType, VdcQueryParametersBase>> operationList, final Throwable caught) { try { if (ignoreFailure(caught)) { return; } logger.log(Level.SEVERE, "Failed to execute runMultipleQueries: " + caught, caught); //$NON-NLS-1$ FrontendMultipleQueryAsyncResult f = new FrontendMultipleQueryAsyncResult(queryTypeList, queryParamsList, null); failureEventHandler(caught); callback.executed(f); } finally { fireAsyncQueryFailedEvent(state); } } }; List<VdcOperation<?, ?>> operationList = new ArrayList<>(); for (int i = 0; i < queryTypeList.size(); i++) { VdcQueryParametersBase parameters = queryParamsList.get(i); parameters.setRefresh(false); // Why do we do this? initQueryParamsFilter(parameters); operationList.add(new VdcOperation<VdcQueryType, VdcQueryParametersBase>(queryTypeList.get(i), parameters, true, multiCallback, false)); } fireAsyncOperationStartedEvent(state); getOperationManager().addOperationList(operationList); } /** * Run an action of the specified action type using the passed in parameters. No object state. * @param actionType The action type of the action to perform. * @param parameters The parameters of the action. * @param callback The callback to call when the action is completed. */ public void runAction(final VdcActionType actionType, final VdcActionParametersBase parameters, final IFrontendActionAsyncCallback callback) { runAction(actionType, parameters, callback, null); } /** * Run an action of the specified action type using the passed in parameters, also pass in a state object. * @param actionType The action type of the action to perform. * @param parameters The parameters of the action. * @param callback The callback to call when the action is completed. * @param state The state object. */ public void runAction(final VdcActionType actionType, final VdcActionParametersBase parameters, final IFrontendActionAsyncCallback callback, final Object state) { runAction(actionType, parameters, callback != null ? callback : NULLABLE_ASYNC_CALLBACK, state, true); } /** * {@code RunAction} without callback. * @param actionType The action type of the action to run. * @param parameters The parameters to the action. * @param showErrorDialog Whether to show a pop-up dialog with the error or not. */ public void runAction(final VdcActionType actionType, final VdcActionParametersBase parameters, final boolean showErrorDialog) { runAction(actionType, parameters, Frontend.NULLABLE_ASYNC_CALLBACK, null, showErrorDialog); } /** * {@code RunAction} without callback. * @param actionType The action type of the action to run. * @param parameters The parameters to the action. */ public void runAction(final VdcActionType actionType, final VdcActionParametersBase parameters) { runAction(actionType, parameters, Frontend.NULLABLE_ASYNC_CALLBACK); } /** * Run an action of the specified action type using the passed in parameters. No object state. * @param actionType The action type of the action to perform. * @param parameters The parameters of the action. * @param callback The callback to call when the action is completed. * @param showErrorDialog Whether to show a pop-up dialog with the error or not. */ public void runAction(final VdcActionType actionType, final VdcActionParametersBase parameters, final IFrontendActionAsyncCallback callback, final boolean showErrorDialog) { runAction(actionType, parameters, callback, null, showErrorDialog); } /** * Run an action of the specified action type using the passed in parameters, also pass in a state object. * @param actionType The action type of the action to perform. * @param parameters The parameters of the action. * @param callback The callback to call when the action is completed. * @param state The state object. * @param showErrorDialog Whether to show a pop-up dialog with the error or not. */ public void runAction(final VdcActionType actionType, final VdcActionParametersBase parameters, final IFrontendActionAsyncCallback callback, final Object state, final boolean showErrorDialog) { VdcOperation<VdcActionType, VdcActionParametersBase> operation = new VdcOperation<>( actionType, parameters, new VdcOperationCallback<VdcOperation<VdcActionType, VdcActionParametersBase>, VdcReturnValueBase>() { @Override public void onSuccess(final VdcOperation<VdcActionType, VdcActionParametersBase> operation, final VdcReturnValueBase result) { logger.finer("Frontend: sucessfully executed runAction, determining result!"); //$NON-NLS-1$ handleActionResult(actionType, parameters, result, callback != null ? callback : NULLABLE_ASYNC_CALLBACK, state, showErrorDialog); fireAsyncActionSucceededEvent(state); } @Override public void onFailure(final VdcOperation<VdcActionType, VdcActionParametersBase> operation, final Throwable caught) { if (ignoreFailure(caught)) { return; } logger.log(Level.SEVERE, "Failed to execute runAction: " + caught, caught); //$NON-NLS-1$ failureEventHandler(caught); FrontendActionAsyncResult f = new FrontendActionAsyncResult(actionType, parameters, null, state); if (callback != null) { callback.executed(f); } fireAsyncActionFailedEvent(state); } }); fireAsyncOperationStartedEvent(state); getOperationManager().addOperation(operation); } /** * Identical to 5 parameter RunMultipleAction, but isRunOnlyIfAllValidationPass is false. * @param actionType The action type of the actions to run. * @param parameters The parameters to the actions. * @param callback The callback to call after the operation happens. * @param state A state object. */ public void runMultipleAction(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final IFrontendMultipleActionAsyncCallback callback, final Object state) { runMultipleAction(actionType, parameters, false, callback, state); } /** * RunMultipleActions without passing state. * @param actionType The type of action to perform. * @param parameters The parameters of the action. */ public void runMultipleAction(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final IFrontendMultipleActionAsyncCallback callback) { runMultipleAction(actionType, parameters, callback, null); } /** * RunMultipleActions without passing in a callback or state. * @param actionType The type of action to perform. * @param parameters The parameters of the action. */ public void runMultipleAction(final VdcActionType actionType, final List<VdcActionParametersBase> parameters) { runMultipleAction(actionType, parameters, null, null); } /** * RunMultipleActions without passing state. * @param actionType The type of action to perform. * @param parameters The parameters of the action. * @param callback The callback to call after the operation happens. * @param showErrorDialog Should we show an error dialog? * @param waitForResult a flag to return the result after running the whole action and not just the can do actions. */ public void runMultipleAction(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final IFrontendMultipleActionAsyncCallback callback, final boolean showErrorDialog, final boolean waitForResult) { runMultipleAction(actionType, parameters, false, callback, null, showErrorDialog, waitForResult); } /** * Run multiple without passing <code>showErrorDialog</code> and <code>waitForResult</code> * @param actionType The action type. * @param parameters The list of parameters. * @param isRunOnlyIfAllValidationPass A flag to only run the actions if all can be completed. * @param callback The callback to call when the operation completes. * @param state The state. */ public void runMultipleAction(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final boolean isRunOnlyIfAllValidationPass, final IFrontendMultipleActionAsyncCallback callback, final Object state) { runMultipleAction(actionType, parameters, isRunOnlyIfAllValidationPass, callback, state, true, false); } /** * Run multiple actions using the same {@code VdcActionType}. * @param actionType The action type. * @param parameters The list of parameters. * @param isRunOnlyIfAllValidationPass A flag to only run the actions if all can be completed. * @param callback The callback to call when the operation completes. * @param state The state. * @param showErrorDialog Should we show an error dialog? * @param waitForResult a flag to return the result after running the whole action and not just the can do actions. */ public void runMultipleAction(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final boolean isRunOnlyIfAllValidationPass, final IFrontendMultipleActionAsyncCallback callback, final Object state, final boolean showErrorDialog, final boolean waitForResult) { VdcOperationCallbackList<VdcOperation<VdcActionType, VdcActionParametersBase>, List<VdcReturnValueBase>> multiCallback = new VdcOperationCallbackList<VdcOperation<VdcActionType, VdcActionParametersBase>, List<VdcReturnValueBase>>() { @Override public void onSuccess(final List<VdcOperation<VdcActionType, VdcActionParametersBase>> operationList, final List<VdcReturnValueBase> resultObject) { logger.finer("Frontend: successfully executed runMultipleAction, determining result!"); //$NON-NLS-1$ ArrayList<VdcReturnValueBase> failed = new ArrayList<>(); for (VdcReturnValueBase v : resultObject) { if (!v.isValid()) { failed.add(v); } } if (showErrorDialog && !failed.isEmpty()) { translateErrors(failed); getEventsHandler().runMultipleActionFailed(actionType, failed); } if (callback != null) { callback.executed(new FrontendMultipleActionAsyncResult(actionType, parameters, resultObject, state)); } fireAsyncActionSucceededEvent(state); } @Override public void onFailure(final List<VdcOperation<VdcActionType, VdcActionParametersBase>> operation, final Throwable caught) { if (ignoreFailure(caught)) { return; } logger.log(Level.SEVERE, "Failed to execute runMultipleAction: " + caught, caught); //$NON-NLS-1$ failureEventHandler(caught); if (callback != null) { callback.executed(new FrontendMultipleActionAsyncResult(actionType, parameters, null, state)); } fireAsyncActionFailedEvent(state); } }; List<VdcOperation<?, ?>> operationList = new ArrayList<>(); for (VdcActionParametersBase parameter: parameters) { VdcOperation<VdcActionType, VdcActionParametersBase> operation = new VdcOperation<>( actionType, parameter, !waitForResult, multiCallback, isRunOnlyIfAllValidationPass); operationList.add(operation); } fireAsyncOperationStartedEvent(state); if (operationList.isEmpty()) { //Someone called run multiple actions with a single action without parameters. The backend will return //an empty return value as there are no parameters, so we can skip the round trip to the server and return //it ourselves. if (scheduler == null) { scheduler = Scheduler.get(); } scheduler.scheduleDeferred(() -> { if (callback != null) { List<VdcReturnValueBase> emptyResult = new ArrayList<>(); callback.executed(new FrontendMultipleActionAsyncResult(actionType, parameters, emptyResult, state)); } }); } else { getOperationManager().addOperationList(operationList); } } /** * A convenience method that calls {@link #runMultipleActions(VdcActionType, List, List, Object, boolean)} with just a single * callback to be called when all actions have succeeded and error aggregation. * * @param actionType The action to be repeated. * @param parameters The parameters of each action. * @param successCallback The callback to be executed when all actions have succeeded. * @param state State object * @param runCallbacksOnEmptyRun Whether to run the callback or not even if no commands were run */ public void runMultipleActions(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final IFrontendActionAsyncCallback successCallback, final Object state, final boolean runCallbacksOnEmptyRun) { if (parameters == null || parameters.isEmpty()) { if (runCallbacksOnEmptyRun && successCallback != null) { successCallback.executed(new FrontendActionAsyncResult(actionType, null, null, state)); } return; } int n = parameters.size(); IFrontendActionAsyncCallback[] callbacks = new IFrontendActionAsyncCallback[n]; callbacks[n - 1] = successCallback; runMultipleActions(actionType, parameters, new LinkedList<IFrontendActionAsyncCallback>(Arrays.asList(callbacks)), state, true); } /** * A convenience method that calls {@link #runMultipleActions(VdcActionType, List, IFrontendActionAsyncCallback, Object, boolean)} * running callbacks even on empty run. * * @param actionType The action to be repeated. * @param parameters The parameters of each action. * @param successCallback The callback to be executed when all actions have succeeded. * @param state State object */ public void runMultipleActions(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final IFrontendActionAsyncCallback successCallback, final Object state) { runMultipleActions(actionType, parameters, successCallback, state, true); } /** * Overloaded method for {@link #runMultipleActions(VdcActionType, List, IFrontendActionAsyncCallback, Object, boolean)} with * state = null and running callbacks even on empty run. * @param actionType The action type of the actions. * @param parameters A list of parameters, once for each action. * @param successCallback The callback to call on success. */ public void runMultipleActions(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final IFrontendActionAsyncCallback successCallback) { runMultipleActions(actionType, parameters, successCallback, null, true); } /** * A convenience method that calls * {@link #runMultipleActions(List, List, List, IFrontendActionAsyncCallback, Object, boolean)} with just the one * VdcActionType for all actions. * * @param actionType The action to be repeated. * @param parameters The parameters of each action. * @param callbacks The callback to be executed upon the success of each action. * @param state The state. * @param aggregateErrors Whether error messages should be aggregated. */ public void runMultipleActions(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final List<IFrontendActionAsyncCallback> callbacks, final Object state, boolean aggregateErrors) { if (parameters == null || parameters.isEmpty()) { return; } VdcActionType[] actionTypes = new VdcActionType[parameters.size()]; Arrays.fill(actionTypes, actionType); runMultipleActions(new LinkedList<VdcActionType>(Arrays.asList(actionTypes)), parameters, callbacks, null, state, aggregateErrors); } /** * Overloaded method for {@link #runMultipleActions(VdcActionType, List, List, Object, boolean)} with state = null * and error aggregation. * * @param actionType The action to be repeated. * @param parameters The parameters of each action. * @param callbacks A list of callbacks. */ public void runMultipleActions(final VdcActionType actionType, final List<VdcActionParametersBase> parameters, final List<IFrontendActionAsyncCallback> callbacks) { runMultipleActions(actionType, parameters, callbacks, null, true); } /** * Overloaded method for * {@link #runMultipleActions(List, List, List, IFrontendActionAsyncCallback, Object, boolean)} with error * message aggregation. */ public void runMultipleActions(final List<VdcActionType> actionTypes, final List<VdcActionParametersBase> parameters, final List<IFrontendActionAsyncCallback> callbacks, final IFrontendActionAsyncCallback failureCallback, final Object state) { runMultipleActions(actionTypes, parameters, callbacks, failureCallback, state, true); } /** * This method allows us to run a transaction like set of actions. If one * fails the rest do not get executed. * @param actionTypes The list of actions to execute. * @param parameters The list of parameters, must match the number of actions. * @param callbacks The list of callbacks, the number must match the number of actions. * @param failureCallback The callback to call in case of failure. * @param state The state. * @param aggregateErrors Whether error messages should be aggregated. */ public void runMultipleActions(final List<VdcActionType> actionTypes, final List<VdcActionParametersBase> parameters, final List<IFrontendActionAsyncCallback> callbacks, final IFrontendActionAsyncCallback failureCallback, final Object state, boolean aggregateErrors) { runMultipleActions(actionTypes, parameters, callbacks, failureCallback, state, aggregateErrors, aggregateErrors ? new ArrayList<VdcActionType>() : null, aggregateErrors ? new ArrayList<VdcReturnValueBase>() : null); } private void runMultipleActions(final List<VdcActionType> actionTypes, final List<VdcActionParametersBase> parameters, final List<IFrontendActionAsyncCallback> callbacks, final IFrontendActionAsyncCallback failureCallback, final Object state, final boolean aggregateErrors, final List<VdcActionType> failedActions, final List<VdcReturnValueBase> failedReturnValues) { if (actionTypes.isEmpty() || parameters.isEmpty() || callbacks.isEmpty()) { if (aggregateErrors && failedReturnValues != null && !failedReturnValues.isEmpty()) { getEventsHandler().runMultipleActionsFailed(failedActions, failedReturnValues); } return; } runAction(actionTypes.get(0), parameters.get(0), result -> { VdcReturnValueBase returnValue = result.getReturnValue(); boolean success = returnValue != null && returnValue.getSucceeded(); if (success || failureCallback == null) { IFrontendActionAsyncCallback callback = callbacks.get(0); if (callback != null) { callback.executed(result); } if (aggregateErrors && returnValue != null && (!returnValue.isValid() || !returnValue.getSucceeded())) { failedActions.add(actionTypes.get(0)); failedReturnValues.add(returnValue); } actionTypes.remove(0); parameters.remove(0); callbacks.remove(0); runMultipleActions(actionTypes, parameters, callbacks, failureCallback, state, aggregateErrors, failedActions, failedReturnValues); } else { failureCallback.executed(result); } }, state, !aggregateErrors || failureCallback != null); } /** * Log off the currently logged in user. * @param callback The callback to call when the user is logged off. */ public void logoffAsync(final AsyncQuery<VdcReturnValueBase> callback) { logger.finer("Frontend: Invoking async logoff."); //$NON-NLS-1$ getOperationManager().logoutUser(new UserCallback<VdcReturnValueBase>() { @Override public void onSuccess(final VdcReturnValueBase result) { logger.finer("Succesful returned result from logoff."); //$NON-NLS-1$ callback.getAsyncCallback().onSuccess(result); } @Override public void onFailure(final Throwable caught) { if (ignoreFailure(caught)) { return; } logger.log(Level.SEVERE, "Failed to execute logoff: " + caught, caught); //$NON-NLS-1$ getEventsHandler().runQueryFailed(null); failureEventHandler(caught); callback.getAsyncCallback().onSuccess(null); } }); } /** * Checks if the user is logged. * @return {@code true} if the user is logged in, false otherwise. */ public Boolean getIsUserLoggedIn() { return getLoggedInUser() != null; } /** * Initializes the currently logged in user. * @param loggedUser A {@code DbUser} object. */ public void initLoggedInUser(DbUser loggedUser) { this.currentUser = loggedUser; } /** * Get the logged in user. * @return A {@code DbUser} object defining the user. */ public DbUser getLoggedInUser() { return currentUser; } /** * The user was logged in externally, we need to tell the frontend the user is logged in, otherwise it will * reject any operations that are not public. * @param loggedInUser The {@code VdcUser} object defining the user. */ public void setLoggedInUser(final DbUser loggedInUser) { this.currentUser = loggedInUser; } public void storeInHttpSession(final String key, final String value) { getOperationManager().storeInHttpSession(key, value); } public void retrieveFromHttpSession(final String key, final StorageCallback callback) { getOperationManager().retrieveFromHttpSession(key, callback); } /** * Handle the result(s) of an action. * @param actionType The action type. * @param parameters The parameters of the action. * @param result The result of the action. * @param callback The callback to call. * @param state The state before the action happened. * @param showErrorDialog Should we show an error dialog? */ void handleActionResult(final VdcActionType actionType, final VdcActionParametersBase parameters, final VdcReturnValueBase result, final IFrontendActionAsyncCallback callback, final Object state, final boolean showErrorDialog) { logger.log(Level.FINER, "Retrieved action result from RunAction."); //$NON-NLS-1$ FrontendActionAsyncResult f = new FrontendActionAsyncResult(actionType, parameters, result, state); boolean failedOnValidate = !result.isValid(); if (failedOnValidate) { result.setValidationMessages((ArrayList<String>) translateError(result)); } else if (!result.getSucceeded()) { EngineFault fault = result.getFault(); String message = result.getExecuteFailedMessages().size() > 1 ? translateExecuteFailedMessages(result.getExecuteFailedMessages()) : translateEngineFault(fault); fault.setMessage(message); if (showErrorDialog && result.getIsSyncronious() && getEventsHandler() != null) { getEventsHandler().runActionExecutionFailed(actionType, fault); } } callback.executed(f); // 'runActionExecutionFailed' invokes an error pop-up displaying, therefore calling 'failureEventHandler' is // only needed for validate failure if (showErrorDialog && failedOnValidate && (getEventsHandler() != null) && getEventsHandler().isRaiseErrorModalPanel(actionType, result.getFault())) { ArrayList<String> messages = result.getValidationMessages(); failureEventHandler(result.getDescription(), messages.isEmpty() ? Collections.singletonList(getConstants().noValidateMessage()) : messages); //$NON-NLS-1$ } } @Override public void fireEvent(GwtEvent<?> event) { eventBus.fireEvent(event); } private void fireAsyncOperationStartedEvent(Object target) { AsyncOperationStartedEvent.fire(this, target); } private void fireAsyncQuerySucceededEvent(Object target) { AsyncOperationCompleteEvent.fire(this, target, false, true); } private void fireAsyncQueryFailedEvent(Object target) { AsyncOperationCompleteEvent.fire(this, target, false, false); } private void fireAsyncActionSucceededEvent(Object target) { AsyncOperationCompleteEvent.fire(this, target, true, true); } private void fireAsyncActionFailedEvent(Object target) { AsyncOperationCompleteEvent.fire(this, target, true, false); } /** * Translate the action failure fault. * @param fault The fault to translate. * @return A translated string defining the reason for the failure. */ public String translateEngineFault(final EngineFault fault) { return getVdsmErrorsTranslator().translateErrorTextSingle(fault.getError() == null ? fault.getMessage() : fault.getError().toString()); } public String translateExecuteFailedMessages(ArrayList<String> executeFailedMessages) { return getVdsmErrorsTranslator().translateErrorText(executeFailedMessages).get(0); } /** * Getter for the Application error translator. * @return An {@code ErrorTranslator} that can translate application errors. */ public ErrorTranslator getAppErrorsTranslator() { return validateErrorsTranslator; } /** * Getter for the VDSM error translator. * @return The {@code ErrorTranslator} */ public ErrorTranslator getVdsmErrorsTranslator() { return vdsmErrorsTranslator; } /** * Check if we should ignore the passed in {@code Throwable}. * @param caught The {@code Throwable} to check. * @return {@code true} if the {@code Throwable} should be ignore, false otherwise. */ protected boolean ignoreFailure(final Throwable caught) { // ignore escape key if (caught instanceof StatusCodeException && ((StatusCodeException) caught).getStatusCode() == 0) { return true; } return false; } /** * Getter for the frontend failure event. * @return an {@code Event} for the frontend failure. */ public Event<FrontendFailureEventArgs> getFrontendFailureEvent() { return frontendFailureEvent; } /** * Getter for the frontend not logged in event. * @return an {@code Event} for the not logged in event. */ public Event<EventArgs> getFrontendNotLoggedInEvent() { return frontendNotLoggedInEvent; } /** * Getter for the events handler. * @return The events handler. */ public IFrontendEventsHandler getEventsHandler() { return eventsHandler; } /** * Setter for the events handler. * @param frontendEventsHandler The new events handler. */ public void setEventsHandler(final IFrontendEventsHandler frontendEventsHandler) { this.eventsHandler = frontendEventsHandler; } /** * Translate application errors and store the translated messages back in return values. * @param errors A list of {@code VdcReturnValueBase}s. */ private void translateErrors(final Collection<VdcReturnValueBase> errors) { for (VdcReturnValueBase retVal : errors) { if (!retVal.isValid()) { retVal.setValidationMessages((ArrayList<String>) translateError(retVal)); } else if (!retVal.getSucceeded()) { EngineFault fault = retVal.getFault(); fault.setMessage(translateEngineFault(fault)); } } } /** * Translate a single application error and store the translated message back in the return value object. * @param error The {@code VdcReturnValueBase} error object. * @return A {@code List} of translated messages. */ private List<String> translateError(final VdcReturnValueBase error) { return getAppErrorsTranslator().translateErrorText(error.getValidationMessages()); } /** * Call failure event handler based on the {@code Throwable} passed in. * @param caught The {@code Throwable} */ private void failureEventHandler(final Throwable caught) { String errorMessage; if (caught instanceof StatusCodeException) { errorMessage = getConstants().requestToServerFailedWithCode() + ": " //$NON-NLS-1$ + ((StatusCodeException) caught).getStatusCode(); } else { errorMessage = getConstants().requestToServerFailed() + ": " + caught.getLocalizedMessage(); //$NON-NLS-1$ } failureEventHandler(null, Collections.singletonList(errorMessage)); } /** * Call failure event handler with the passed in description and list of error messages. * @param description A description of the failure (nullable) * @param errorMessages A list of error messages. */ private void failureEventHandler(final String description, final List<String> errorMessages) { ArrayList<Message> messages = new ArrayList<>(); for (String errorMessage : errorMessages) { handleNotLoggedInEvent(errorMessage); messages.add(new Message(description, errorMessage)); } frontendFailureEvent.raise(Frontend.class, new FrontendFailureEventArgs(messages)); } /** * Setter for constants. * @param uiConstants The constants to set. */ void setConstants(final UIConstants uiConstants) { constants = uiConstants; } /** * Get a copy of the UIConstants. * @return The UIConstants object. */ private UIConstants getConstants() { if (constants == null) { constants = ConstantsManager.getInstance().getConstants(); } return constants; } /** * Call the not logged in event handler. * @param errorMessage The error message. */ private void handleNotLoggedInEvent(final String errorMessage) { if (errorMessage != null && errorMessage.equals("USER_IS_NOT_LOGGED_IN")) { //$NON-NLS-1$ frontendNotLoggedInEvent.raise(Frontend.class, EventArgs.EMPTY); } } /** * Setter for filterQueries. * @param shouldFilterQueries should queries be filtered, true or false. */ public void setFilterQueries(final boolean shouldFilterQueries) { this.filterQueries = shouldFilterQueries; } /** * set the filterQueries parameter. * @param parameters The parameters to set. */ private void initQueryParamsFilter(final VdcQueryParametersBase parameters) { parameters.setFiltered(filterQueries); } /** * Translate and show popup for the actions errors */ public void runMultipleActionsFailed(Map<VdcActionType, List<VdcReturnValueBase>> failedActionsMap, MessageFormatter messageFormatter) { Collection<VdcReturnValueBase> failedResults = new ArrayList<>(); for (List<VdcReturnValueBase> vdcActionTypeResults : failedActionsMap.values()) { failedResults.addAll(vdcActionTypeResults); } translateErrors(failedResults); getEventsHandler().runMultipleActionsFailed(failedActionsMap, messageFormatter); } }