/* * Copyright 2014 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.rpc.client.interceptor; import com.google.gwt.user.client.rpc.AsyncCallback; import com.gwtplatform.dispatch.client.ExecuteCommand; import com.gwtplatform.dispatch.rpc.shared.Action; import com.gwtplatform.dispatch.shared.DispatchRequest; /** * Instances of this interface will handle specific types of action classes on the client. * <p/> * When a call is executed, the {@link RpcInterceptor} that has been registered with the bound {@link * RpcInterceptorRegistry} is called and {@link com.gwtplatform.dispatch.rpc.shared.DispatchAsync DispatchAsync} does * not automatically send the command over HTTP to the server. * <p/> * Interceptors provide a number of flexible options: <ul> <li>The action can be modified before sending the action to * the server.</li> <li>A result can be returned without contacting the server.</li> <li>The result can be modified or * processed after it is returned from the server.</li> <li>The {@link RpcInterceptor} can take over and communicate * directly with the server, possibly using a different mechanism.</li> </ul> * <p/> * <b>Important!</b> If your interceptor makes asynchronous calls, be careful with your use of fields as a second call * your interceptor could be made while it is waiting for the asynchronous call to return. * <p/> * <h3>Caching Interceptor Example</h3> * <p/> * <pre> * <code> * // Interface of cache singleton * public interface Cache { * <A extends Action<R>, R> R get(A action); * <A extends Action<R>, R> void put(A action, R result); * } * * // Interceptor that injects the cache * public class RetrieveFooInterceptor * extends AbstractCachingInterceptor<RetrieveFooAction, RetrieveFooResult> { * {@literal}@Inject * RetrieveFooInterceptor(Cache cache) { * super(RetrieveFooAction.class, cache); * } * } * * // abstract interceptor that: * // - first checks cache and returns result immediately if found in cache * // - executes command on server * // - saves result to cache before returning it * public abstract class AbstractCachingInterceptor<A extends Action<R>, R> * extends AbstractInterceptor<A, R> { * * private final Cache cache; * * public AbstractCachingInterceptor(Class<A> actionType, Cache cache) { * super(actionType); * this.cache = cache; * } * * {@literal}@Override * public DispatchRequest execute(final A action, final AsyncCallback<R> resultCallback, * ExecuteCommand<A, R> executeCommand) { * R cacheResult = cache.get(action); * if (cacheResult != null) { * resultCallback.onSuccess(cacheResult); * return new CompletedDispatchRequest(); * } else { * return executeCommand.execute(action, new AsyncCallback<R>() { * {@literal}@Override * public void onSuccess(R result) { * if(!request.isCancelled()) { * cache.put(action, result); * resultCallback.onSuccess(result); * } * } * * {@literal}@Override * public void onFailure(Throwable caught) { * resultCallback.onFailure(caught); * } * }); * } * } * } * </code> * </pre> * * @param <A> The type of the action. * @param <R> The type of the result. */ public interface RpcInterceptor<A, R> { /** * @return The type of action supported by this interceptor. */ Class<A> getActionType(); /** * Ensures this intercepted call can be executed. * * @param action the action to test against. * @return true if this action can be executed, false if not. */ boolean canExecute(Action<?> action); /** * Handles the specified action. * <p/> * If the interceptor makes asynchronous calls, it is recommended that you confirm that this request has not been * cancelled after returning by calling {@link com.gwtplatform.dispatch.client.DelegatingDispatchRequest#isPending() * DelegatingDispatchRequest#isPending()} against the request parameter. * * @param action The action to execute. * @param resultCallback The callback to use to communicate the result of the action. Unless the request is * cancelled, you must invoke {@link AsyncCallback#onSuccess(Object)} on this callback once * you have obtained the result. If any failure occurs call {@link * AsyncCallback#onFailure(Throwable)}. * @param executeCommand Call {@link ExecuteCommand#execute(Object, AsyncCallback)} on this object to send the * action over to the server. As a parameter you can pass {@code resultCallback} or your * custom {@link AsyncCallback} if you want to process the result. * @return A {@link DispatchRequest} object. Never return {@code null}, instead return a new {@link * com.gwtplatform.dispatch.client.CompletedDispatchRequest CompletedDispatchRequest} if you executed, cancelled or * ignored the action. */ DispatchRequest execute( A action, AsyncCallback<R> resultCallback, ExecuteCommand<A, AsyncCallback<R>> executeCommand); /** * Undoes the specified action if supported. * <p/> * If the interceptor makes asynchronous calls, it is recommended that you confirm that this request has not been * cancelled after returning by calling * {@link com.gwtplatform.dispatch.client.DelegatingDispatchRequest#isPending()} * against the request parameter. * * @param action The action to undo. * @param result The result to undo. * @param callback The callback to use to indicate when the action has been undone. Unless the request is * cancelled, you must invoke {@link AsyncCallback#onSuccess} on this callback when you have * successfully undone the action. If any failure occurs call {@link AsyncCallback#onFailure}. * @param undoCommand Call {@link UndoCommand#undo(Object, Object, com.google.gwt.user.client.rpc.AsyncCallback)} on * this object to send the action over to the server via gwt-rpc. As a parameter you can pass * {@code callback} or your custom {@link AsyncCallback} if you want to perform any processing * following the undo. * @return A {@link DispatchRequest} object. Never return {@code null}, instead return a new {@link * com.gwtplatform.dispatch.client.CompletedDispatchRequest} if you executed, cancelled or ignored the action. */ DispatchRequest undo(A action, R result, AsyncCallback<Void> callback, UndoCommand<A, R> undoCommand); }