package klik.client.dispatch;
import java.util.ArrayList;
import java.util.HashMap;
import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.gwtplatform.dispatch.client.DefaultDispatchAsync;
import com.gwtplatform.dispatch.client.ExceptionHandler;
import com.gwtplatform.dispatch.client.actionhandler.ClientActionHandlerRegistry;
import com.gwtplatform.dispatch.shared.Action;
import com.gwtplatform.dispatch.shared.DispatchRequest;
import com.gwtplatform.dispatch.shared.Result;
import com.gwtplatform.dispatch.shared.SecurityCookieAccessor;
/**
* Dispatcher which supports in memory data caching
*/
@Singleton
public class CachingDispatchAsync extends DefaultDispatchAsync {
private static HashMap<Action<Result>, Result> cache = new HashMap<Action<Result>, Result>();
private static HashMap<Action<Result>, ArrayList<AsyncCallback<Result>>> pendingCallbacks =
new HashMap<Action<Result>, ArrayList<AsyncCallback<Result>>>();
@Inject
public CachingDispatchAsync(ExceptionHandler exceptionHandler,
SecurityCookieAccessor securityCookieAccessor,
ClientActionHandlerRegistry registry) {
super(exceptionHandler, securityCookieAccessor, registry);
// TODO Auto-generated constructor stub
}
/**
* Executes the given action and with cache when action is cacheable.
* @return
*/
@Override
public <A extends Action<R>, R extends Result> DispatchRequest execute(final A action, AsyncCallback<R> callback) {
Log.debug("Calling service for " +action);
return super.execute(action, callback);
}
/**
* Executes the give Action. If the Action was executed before it will get fetched from the cache.
*
* @param Action implementation
* @param Result implementation
* @param action the action
* @param callback the callback
*/
@SuppressWarnings("unchecked")
public <A extends Action<R>, R extends Result> void executeWithCache(final A action, final AsyncCallback<R> callback) {
Log.debug("Executing with cache " + action.toString());
final ArrayList<AsyncCallback<Result>> pending = pendingCallbacks.get(action);
// TODO timeout
if (pending != null) {
Log.debug("Command pending for " + action);
pending.add((AsyncCallback<Result>) callback);
return;
}
final Result r = cache.get(action);
if (r != null) {
Log.debug("Cache hit for " + action);
callback.onSuccess((R) r);
} else {
Log.debug("Calling real sevice for " + action);
pendingCallbacks.put((Action<Result>) action, new ArrayList<AsyncCallback<Result>>());
super.execute(action, new AsyncCallback<R>() {
@Override
public void onFailure(Throwable caught) {
Log.debug("Real service call failed " + action);
// Process all pending callbacks for this action
ArrayList<AsyncCallback<Result>> callbacks = pendingCallbacks.remove(action);
for (AsyncCallback<Result> pendingCallback : callbacks) {
pendingCallback.onFailure(caught);
}
callback.onFailure(caught);
}
@Override
public void onSuccess(R result) {
Log.debug("Real service returned successfully " + action);
ArrayList<AsyncCallback<Result>> callbacks = pendingCallbacks.remove(action);
for (AsyncCallback<Result> pendingCallback : callbacks) {
pendingCallback.onSuccess(result);
}
cache.put((Action<Result>) action, (Result) result);
callback.onSuccess(result);
}
});
}
}
/**
* Clear the cache
*/
public void clear() {
cache.clear();
}
/**
* Clear the cache for a specific Action
*
* @param action
*/
@SuppressWarnings("unchecked")
public <A extends Action<R>, R extends Result> void clear(A action) {
cache.put((Action<Result>) action, null);
}
}