com.gwtplatform.dispatch.server.AbstractDispatchImpl Maven / Gradle / Ivy
/**
* Copyright 2011 ArcBees Inc.
*
* 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 com.gwtplatform.dispatch.server;
import java.util.List;
import com.gwtplatform.dispatch.server.actionhandler.ActionHandler;
import com.gwtplatform.dispatch.server.actionhandler.ActionResult;
import com.gwtplatform.dispatch.server.actionhandlervalidator.ActionHandlerValidatorInstance;
import com.gwtplatform.dispatch.server.actionhandlervalidator.ActionHandlerValidatorRegistry;
import com.gwtplatform.dispatch.server.actionvalidator.ActionValidator;
import com.gwtplatform.dispatch.shared.Action;
import com.gwtplatform.dispatch.shared.ActionException;
import com.gwtplatform.dispatch.shared.Result;
import com.gwtplatform.dispatch.shared.ServiceException;
import com.gwtplatform.dispatch.shared.UnsupportedActionException;
/**
* This is the server-side implementation of the {@link Dispatch} service with an arbitrary action type, for which the
* client-side async service is {@link com.gwtplatform.dispatch.shared.DispatchAsync}.
*
* This class is closely related to {@link com.gwtplatform.dispatch.server.guice.DispatchServiceImpl}.
* In fact, this class wouldn't be needed, but we use it
* to workaround a GWT limitation described in {@link com.gwtplatform.dispatch.shared.DispatchAsync}.
*
* @author Christian Goudreau
* @author David Peterson
* @see com.gwtplatform.dispatch.shared.DispatchAsync
* @see com.gwtplatform.dispatch.server.Dispatch
* @see com.gwtplatform.dispatch.server.guice.DispatchImpl
* @see com.gwtplatform.dispatch.shared.DispatchService
* @see com.gwtplatform.dispatch.shared.DispatchServiceAsync
* @see com.gwtplatform.dispatch.server.guice.DispatchServiceImpl
*/
public abstract class AbstractDispatchImpl implements Dispatch {
private static class DefaultExecutionContext implements ExecutionContext {
private final List> actionResults;
private final AbstractDispatchImpl dispatch;
private DefaultExecutionContext(AbstractDispatchImpl dispatch) {
this.dispatch = dispatch;
this.actionResults = new java.util.ArrayList>();
}
@Override
public , R extends Result> R execute(A action) throws ActionException, ServiceException {
R result = dispatch.doExecute(action, this);
actionResults.add(new ActionResult(action, result, true));
return result;
}
@Override
public , R extends Result> void undo(A action, R result) throws ActionException,
ServiceException {
dispatch.doExecute(action, this);
actionResults.add(new ActionResult(action, result, false));
}
/**
* Rolls back all logged executed actions.
*
* @throws ActionException If there is an action exception while rolling back.
* @throws ServiceException If there is a low level problem while rolling back.
*/
private void rollback() throws ActionException, ServiceException {
DefaultExecutionContext ctx = new DefaultExecutionContext(dispatch);
for (int i = actionResults.size() - 1; i >= 0; i--) {
ActionResult, ?> actionResult = actionResults.get(i);
rollback(actionResult, ctx);
}
}
private , R extends Result> void rollback(ActionResult actionResult,
ExecutionContext ctx) throws ActionException,
ServiceException {
if (actionResult.isExecuted()) {
dispatch.doUndo(actionResult.getAction(), actionResult.getResult(), ctx);
} else {
dispatch.doExecute(actionResult.getAction(), ctx);
}
}
}
private static final String actionValidatorMessage = " couldn't allow access to action : ";
private final ActionHandlerValidatorRegistry actionHandlerValidatorRegistry;
protected AbstractDispatchImpl(ActionHandlerValidatorRegistry actionHandlerValidatorRegistry) {
this.actionHandlerValidatorRegistry = actionHandlerValidatorRegistry;
}
@Override
public , R extends Result> R execute(A action) throws ActionException, ServiceException {
DefaultExecutionContext ctx = new DefaultExecutionContext(this);
try {
return doExecute(action, ctx);
} catch (ActionException e) {
ctx.rollback();
throw e;
} catch (ServiceException e) {
ctx.rollback();
throw e;
}
}
@Override
public , R extends Result> void undo(A action, R result) throws ActionException,
ServiceException {
DefaultExecutionContext ctx = new DefaultExecutionContext(this);
try {
doUndo(action, result, ctx);
} catch (ActionException e) {
ctx.rollback();
throw e;
} catch (ServiceException e) {
ctx.rollback();
throw e;
}
}
/**
* Every single action will be executed by this function and validated by the {@link ActionValidator}.
*
* @param Type of associated {@link Action} type.
* @param Type of associated {@link Result} type.
* @param action The {@link Action} to execute
* @param ctx The {@link ExecutionContext} associated with the {@link Action}
* @return The {@link Result} to the client
* @throws ActionException
* @throws ServiceException
*/
private , R extends Result> R doExecute(A action,
ExecutionContext ctx) throws ActionException, ServiceException {
ActionHandler handler = findHandler(action);
ActionValidator actionValidator = findActionValidator(action);
try {
if (actionValidator.isValid(action)) {
return handler.execute(action, ctx);
} else {
throw new ServiceException(actionValidator.getClass().getName() + actionValidatorMessage + action
.getClass().getName());
}
} catch (ActionException e) {
throw e;
} catch (Exception e) {
String newMessage = "Service exception executing action \"" + action.getClass().getSimpleName() + "\", " +
"" + e.toString();
throw new ServiceException(newMessage, e);
}
}
private , R extends Result> void doUndo(A action, R result,
ExecutionContext ctx) throws ActionException, ServiceException {
ActionValidator actionValidator = findActionValidator(action);
ActionHandler handler = findHandler(action);
try {
if (actionValidator.isValid(action)) {
handler.undo(action, result, ctx);
} else {
throw new ServiceException(actionValidator.getClass().getName() + actionValidatorMessage + action
.getClass().getName());
}
} catch (ActionException e) {
throw e;
} catch (Exception cause) {
throw new ServiceException(cause);
}
}
private , R extends Result> ActionValidator findActionValidator(A action) throws
UnsupportedActionException {
ActionHandlerValidatorInstance handlerValidator =
actionHandlerValidatorRegistry.findActionHandlerValidator(action);
if (handlerValidator == null) {
throw new UnsupportedActionException(action);
}
return handlerValidator.getActionValidator();
}
@SuppressWarnings("unchecked")
private , R extends Result> ActionHandler findHandler(A action)
throws UnsupportedActionException {
ActionHandlerValidatorInstance handlerValidator =
actionHandlerValidatorRegistry.findActionHandlerValidator(action);
if (handlerValidator == null) {
throw new UnsupportedActionException(action);
}
return (ActionHandler) handlerValidator.getActionHandler();
}
}