Minor refactorings

This commit is contained in:
Papick Garcia Taboada committed 2016-01-26 08:08:19 +01:00
1 parent 56dd5a9e81
commit 6aff2799c4
28 files changed
+288 -131

No files matched your search

@@ -1,6 +1,5 @@
package com.sap.sailing.dashboards.gwt.server;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -18,11 +17,11 @@ import com.sap.sailing.gwt.ui.server.ProxiedRemoteServiceServlet;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.ResultWrapper;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
import com.sap.sse.gwt.dispatch.client.exceptions.ServerDispatchException;
import com.sap.sse.gwt.dispatch.client.rpcimpl.DispatchRPC;
import com.sap.sse.gwt.dispatch.client.rpcimpl.RequestWrapper;
import com.sap.sse.gwt.dispatch.client.rpcimpl.ResultWrapper;
import com.sap.sse.util.ServiceTrackerFactory;
public class DashboardDispatchRPCServletImpl extends ProxiedRemoteServiceServlet implements DispatchRPC<DashboardDispatchContext> {
@@ -56,9 +55,9 @@ public class DashboardDispatchRPCServletImpl extends ProxiedRemoteServiceServlet
logger.log(Level.WARNING, "Server exception", d);
throw d;
} catch (Throwable t) {
String serverExceptionUUID = UUID.randomUUID().toString();
logger.log(Level.SEVERE, "Uncaught server exception id: " + serverExceptionUUID, t);
throw new ServerDispatchException(serverExceptionUUID, t);
ServerDispatchException dispatchException = new ServerDispatchException(t);
logger.log(Level.SEVERE, "Uncaught server exception id: " + dispatchException.getExceptionId(), t);
throw dispatchException;
} finally {
long duration = System.currentTimeMillis() - start;
final Level logLevel;
@@ -11,7 +11,7 @@ import org.junit.Test;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchAsync;
import com.sap.sse.gwt.dispatch.client.DispatchSystemAsync;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.caching.CachingDispatch;
@@ -263,7 +263,7 @@ public class CacheDispatchTest {
}
}
private class DispatchMock implements DispatchAsync<DispatchContextMock> {
private class DispatchMock implements DispatchSystemAsync<DispatchContextMock> {
@Override
public <R extends Result, A extends Action<R, DispatchContextMock>> void execute(A action,
AsyncCallback<R> callback) throws DispatchException {
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.home.server.servlets;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -15,11 +14,11 @@ import com.sap.sailing.news.EventNewsService;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.ResultWrapper;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
import com.sap.sse.gwt.dispatch.client.exceptions.ServerDispatchException;
import com.sap.sse.gwt.dispatch.client.rpcimpl.DispatchRPC;
import com.sap.sse.gwt.dispatch.client.rpcimpl.RequestWrapper;
import com.sap.sse.gwt.dispatch.client.rpcimpl.ResultWrapper;
import com.sap.sse.util.ServiceTrackerFactory;
public class DispatchRPCServletImpl extends ProxiedRemoteServiceServlet implements DispatchRPC<SailingDispatchContext> {
@@ -52,9 +51,9 @@ public class DispatchRPCServletImpl extends ProxiedRemoteServiceServlet implemen
logger.log(Level.WARNING, "Server exception", d);
throw d;
} catch (Throwable t) {
String serverExceptionUUID = UUID.randomUUID().toString();
logger.log(Level.SEVERE, "Uncaught server exception id: " + serverExceptionUUID, t);
throw new ServerDispatchException(serverExceptionUUID, t);
ServerDispatchException dispatchException = new ServerDispatchException(t);
logger.log(Level.SEVERE, "Uncaught server exception id: " + dispatchException.getExceptionId(), t);
throw dispatchException;
} finally {
long duration = System.currentTimeMillis() - start;
final Level logLevel;
@@ -11,6 +11,8 @@ import java.util.Locale;
import java.util.Map.Entry;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
@@ -51,6 +53,8 @@ import com.sap.sse.shared.media.MediaDescriptor;
import com.sap.sse.shared.media.VideoDescriptor;
public final class HomeServiceUtil {
private static final Logger logger = Logger.getLogger(HomeServiceUtil.class.getName());
public interface EventVisitor {
void visit(EventBase event, boolean onRemoteServer, URL baseURL);
}
@@ -533,7 +537,9 @@ public final class HomeServiceUtil {
try {
return new URL(url.getProtocol(), url.getHost(), url.getPort(), /* file */"");
} catch (MalformedURLException e) {
throw new ServerDispatchException(e);
ServerDispatchException dispatchException = new ServerDispatchException(e);
logger.log(Level.SEVERE, "Uncaught server exception id: " + dispatchException.getExceptionId(), e);
throw dispatchException;
}
}
@@ -3,9 +3,29 @@ package com.sap.sse.gwt.dispatch.client;
import com.google.gwt.core.shared.GwtIncompatible;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
import com.sap.sse.gwt.dispatch.client.impl.DispatchSystem;
/**
* Action interface that defines actions that can be sent to server using a {@link DispatchSystem}.
*
* The execute method will be executed on the server side and is therefore {@link GwtIncompatible}.
*
* The state defined in the action is the state that will be sent over the wire. Any method (other than execute, which
* is GwtIncompatible) can be called both on the client and on the server side.
*
* @param <R>
* @param <CTX>
*/
public interface Action<R extends Result, CTX extends DispatchContext> extends IsSerializable {
/**
* Server side execution of the action. The execute method is GwtIncompatible and will be available only on the
* server side.
*
* @param ctx
* @return
* @throws DispatchException
*/
@GwtIncompatible
R execute(CTX ctx) throws DispatchException;
@@ -2,6 +2,11 @@ package com.sap.sse.gwt.dispatch.client;
import java.util.Collection;
/**
* Collection result that can be reused to send a collection of {@link DTO} instances
*
* @param <T>
*/
public interface CollectionResult<T extends DTO> extends DTO, Result {
Collection<T> getValues();
}
@@ -1,9 +0,0 @@
package com.sap.sse.gwt.dispatch.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface DispatchAsync<CTX extends DispatchContext> {
<R extends Result, A extends Action<R, CTX>> void execute(A action, AsyncCallback<R> callback);
}
@@ -0,0 +1,23 @@
package com.sap.sse.gwt.dispatch.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* Async interface that defines the dispatch system contract.
*
* @param <CTX>
*/
public interface DispatchSystemAsync<CTX extends DispatchContext> {
/**
* Send an action to be executed on the server.
*
* @param action
* the action to be executed on the server
*
* @param callback
* the callback that will called upon success or failure.
*/
<R extends Result, A extends Action<R, CTX>> void execute(A action, AsyncCallback<R> callback);
}
@@ -2,6 +2,11 @@ package com.sap.sse.gwt.dispatch.client;
import com.google.gwt.user.client.rpc.IsSerializable;
/**
* Result marker interface for the dispatch communication pattern.
*
* Results may be reused across different actions.
*/
public interface Result extends IsSerializable {
}
@@ -4,6 +4,11 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.gwt.dispatch.client.caching.HasClientCacheTotalTimeToLive;
/**
* Result implementation that holds a {@link DTO} with a defined time to live expiration.
*
* @param <T>
*/
public class ResultWithTTL<T extends DTO> implements Result, HasClientCacheTotalTimeToLive {
/** max millis the result might be loaded earlier so we do batches **/
@@ -6,15 +6,15 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchAsync;
import com.sap.sse.gwt.dispatch.client.DispatchSystemAsync;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
public class AutomaticBatchingDispatch<CTX extends DispatchContext> implements DispatchAsync<CTX> {
public class AutomaticBatchingDispatch<CTX extends DispatchContext> implements DispatchSystemAsync<CTX> {
private static final Logger LOG = Logger.getLogger(AutomaticBatchingDispatch.class.getName());
private final DispatchAsync<CTX> executionContext;
private final DispatchSystemAsync<CTX> executionContext;
private DispatchCallStack<CTX> currentCallstack;
@@ -29,7 +29,7 @@ public class AutomaticBatchingDispatch<CTX extends DispatchContext> implements D
* the underlying service implementation to use
*
*/
public AutomaticBatchingDispatch(DispatchAsync<CTX> service) {
public AutomaticBatchingDispatch(DispatchSystemAsync<CTX> service) {
this(service, false);
}
@@ -40,9 +40,10 @@ public class AutomaticBatchingDispatch<CTX extends DispatchContext> implements D
*
* @param service
* the underlying service implementation to use
*
* @param processResultsScheduled
* use a {@link Scheduler} to process the results
*/
public AutomaticBatchingDispatch(DispatchAsync<CTX> service, boolean processResultsScheduled) {
public AutomaticBatchingDispatch(DispatchSystemAsync<CTX> service, boolean processResultsScheduled) {
this.executionContext = service;
this.processResultsScheduled = processResultsScheduled;
}
@@ -10,20 +10,20 @@ import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
public class BatchAction<CTX extends DispatchContext> implements Action<BatchResult, CTX> {
public final class BatchAction<CTX extends DispatchContext> implements Action<BatchResult, CTX> {
private static final Logger logger = Logger.getLogger(BatchAction.class.getName());
private Action<?, CTX>[] actions;
private ArrayList<Action<?, CTX>> actions = new ArrayList<Action<?, CTX>>();
@SuppressWarnings("unused")
private BatchAction() {
protected BatchAction() {
}
@SafeVarargs
public BatchAction(Action<?, CTX>... actions) {
this.actions = actions;
public BatchAction(ArrayList<Action<?, CTX>> actions) {
if (actions != null) {
this.actions.addAll(actions);
}
}
public Action<?, CTX>[] getActions() {
public ArrayList<Action<?, CTX>> getActions() {
return actions;
}
@@ -31,7 +31,7 @@ public class BatchAction<CTX extends DispatchContext> implements Action<BatchRes
@GwtIncompatible
public BatchResult execute(CTX ctx) throws DispatchException {
final int nrOfActions = getActions().length;
final int nrOfActions = getActions().size();
final ArrayList<Result> results = new ArrayList<Result>(nrOfActions);
final ArrayList<DispatchException> exceptions = new ArrayList<DispatchException>(nrOfActions);
@@ -1,21 +1,21 @@
package com.sap.sse.gwt.dispatch.client.batching;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.google.gwt.core.shared.GwtIncompatible;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
public class BatchResult implements Result {
private LinkedList<Result> results = new LinkedList<Result>();
public final class BatchResult implements Result {
private ArrayList<Result> results = new ArrayList<>();
private ArrayList<DispatchException> exceptions = new ArrayList<>();
private LinkedList<DispatchException> exceptions = new LinkedList<DispatchException>();
@SuppressWarnings("unused")
private BatchResult() {
protected BatchResult() {
}
@GwtIncompatible
public BatchResult(List<Result> results, List<DispatchException> exceptions) {
this.results.addAll(results);
this.exceptions.addAll(exceptions);
@@ -37,9 +37,20 @@ public class BatchResult implements Result {
return results.get(i);
}
/**
* Retrieves the result at a given position only if it has the right type. Type safety is ensured by comparing class
* name. We cannot use isAssignableFrom because this is GWT/ client code.
*
* @param i
* result position
* @param type
* required result type
* @return result at given position
*/
@SuppressWarnings("unchecked")
public <T extends Result> T getResult(int i, Class<T> type) {
Object result = results.get(i);
if (result != null && type.getName().equals(result.getClass().getName()))
return (T) result;
return null;
@@ -1,6 +1,6 @@
package com.sap.sse.gwt.dispatch.client.batching;
import java.util.LinkedList;
import java.util.ArrayList;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -9,7 +9,7 @@ import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
/**
* Convenience class used to aggregate {@link Call} instances.
* Convenience class used to aggregate {@link DispatchCall} instances.
*
* The class and all methods are private, its use is internal only.
*
@@ -19,7 +19,7 @@ public final class DispatchCallStack<CTX extends DispatchContext> {
/**
* The list containg all queued actions.
*/
private final LinkedList<DispatchCall<?, ?, CTX>> actionQueue = new LinkedList<DispatchCall<?, ?, CTX>>();
private final ArrayList<DispatchCall<?, ?, CTX>> actionQueue = new ArrayList<DispatchCall<?, ?, CTX>>();
/**
* The batch action holding the all the actions build in the queue. This instance variable is used to lazy
@@ -30,7 +30,8 @@ public final class DispatchCallStack<CTX extends DispatchContext> {
/**
* Add a call to the queue. Can only be called as long as the batch action has not been accessed/ initialized.
*
* @param call
* @param action
* @param callback
*/
public <A extends Action<R, CTX>, R extends Result> void addCall(A action, AsyncCallback<R> callback) {
assert (this.batchAction == null);
@@ -38,16 +39,15 @@ public final class DispatchCallStack<CTX extends DispatchContext> {
}
/**
* Return the aggregated calls as {@link BatchAction} instance. The instance is lazy initialized.
* Return the aggregated calls as {@link BatchAction} instance. The instance is initialized lazily.
*
* @return
*/
public BatchAction<CTX> getBatchAction() {
if (this.batchAction == null) {
@SuppressWarnings("unchecked")
Action<?, CTX>[] actions = new Action[this.actionQueue.size()];
for (int loop = 0; loop < actions.length; loop++) {
actions[loop] = this.actionQueue.get(loop).getAction();
ArrayList<Action<?, CTX>> actions = new ArrayList<Action<?, CTX>>();
for (DispatchCall<?, ?, CTX> dispatchCall : this.actionQueue) {
actions.add(dispatchCall.getAction());
}
this.batchAction = new BatchAction<CTX>(actions);
}
@@ -67,16 +67,16 @@ public final class DispatchCallStack<CTX extends DispatchContext> {
}
/**
* Process results for the callstack. TODO: check generics usage, get rid of supress warnings
* Process results for the callstack.
*
* @param batchResult
* @param processResultsScheduled
* use a scheduler to execute the calls
*/
public void processResult(BatchResult batchResult, boolean processResultsScheduled) {
assert (batchResult != null);
int loop = 0;
for (@SuppressWarnings("rawtypes")
DispatchCall call : this.actionQueue) {
for (DispatchCall<?, ?, CTX> call : this.actionQueue) {
Result result = batchResult.getResult(loop);
Throwable exception = batchResult.getException(loop);
@@ -90,17 +90,24 @@ public final class DispatchCallStack<CTX extends DispatchContext> {
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
/**
* Scheduled command wrapping the callback method call.
*/
private class CallbackCommand implements ScheduledCommand {
@SuppressWarnings("rawtypes")
private final DispatchCall call;
private final Result result;
private final Throwable exception;
@SuppressWarnings("rawtypes")
public CallbackCommand(DispatchCall call, Result result, Throwable exception) {
this.call = call;
this.result = result;
this.exception = exception;
}
@SuppressWarnings("unchecked")
@Override
public void execute() {
if (hasFailed()) {
@@ -1,5 +1,8 @@
package com.sap.sse.gwt.dispatch.client.batching;
/**
* Marker interface to identify actions that must not be batched
*/
public interface NonBatchableAction {
}
@@ -8,6 +8,9 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
/**
* Convenience to enqueue and execute ScheduledCommands using the GWT scheduler.
*/
public class SplitScheduler {
private static final SplitScheduler INSTANCE = new SplitScheduler();
@@ -16,6 +19,12 @@ public class SplitScheduler {
private boolean scheduled = false;
private SplitScheduler() {
}
/**
* Repeating command instance that picks up the next element in the queue of pending commands and executes it.
*/
private final RepeatingCommand repeatingCommand = new RepeatingCommand() {
@Override
public boolean execute() {
@@ -33,13 +42,15 @@ public class SplitScheduler {
}
};
private SplitScheduler() {
}
public static SplitScheduler get() {
return INSTANCE;
}
/**
* Add a command to the queue and executes it using the gwt scheduler.
*
* @param command
*/
public void schedule(ScheduledCommand command) {
pendingCommands.add(command);
@@ -8,29 +8,42 @@ import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.common.Duration;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchAsync;
import com.sap.sse.gwt.dispatch.client.DispatchSystemAsync;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
/**
* Delegating dispatch implementation that caches results if actions implement IsClientCache interface.
*
* @author pgtaboada
*/
public final class CachingDispatch<CTX extends DispatchContext> implements DispatchAsync<CTX> {
public final class CachingDispatch<CTX extends DispatchContext> implements DispatchSystemAsync<CTX> {
private static final Logger LOG = Logger.getLogger(CachingDispatch.class.getName());
private final CacheCleanupTask invalidationTask;
private final HashMap<String, ResultHolder> resultsCache = new HashMap<>();
private final DispatchAsync<CTX> dispatch;
private final DispatchSystemAsync<CTX> dispatch;
private final int defaultTimeToLive;
public CachingDispatch(DispatchAsync<CTX> service) {
/**
* Create new caching dispatch instance with default values.
*
* @param service
* the underlying service used to make dispatch calls
*/
public CachingDispatch(DispatchSystemAsync<CTX> service) {
this(service, true, (int) Duration.ONE_MINUTE.times(3).asMillis());
}
public CachingDispatch(DispatchAsync<CTX> service, boolean enableCleanup, int defaultTimeToLive) {
/**
* Create new caching dispatch instance with given parameters
*
* @param service
* the underlying service used to make dispatch calls
* @param enableCleanup
* enable cleanup cached elements
* @param defaultTimeToLive
* default cached element time to live
*/
public CachingDispatch(DispatchSystemAsync<CTX> service, boolean enableCleanup, int defaultTimeToLive) {
this.dispatch = service;
this.defaultTimeToLive = defaultTimeToLive;
if (enableCleanup) {
@@ -60,21 +73,23 @@ public final class CachingDispatch<CTX extends DispatchContext> implements Dispa
};
/**
* Perform dispatch call with intercepting callback.
* Perform dispatch call with using an intercepting callback for client cacheable commands.
*
* @param action
* @param callback
*/
private <R extends Result, A extends Action<R, CTX>> void executeAndCache(final A action,
final AsyncCallback<R> callback) {
dispatch.execute(action, new InterceptingCallback<>(action, callback));
if (action instanceof IsClientCacheable) {
dispatch.execute(action, new InterceptingCallback<>(action, callback));
} else {
dispatch.execute(action, callback);
}
}
/**
* Intercepting callback that stores the result into cache.
*
* @author pgtaboada
*
* @param <R>
* @param <A>
*/
@@ -89,26 +104,35 @@ public final class CachingDispatch<CTX extends DispatchContext> implements Dispa
@Override
public void onSuccess(R result) {
if (action instanceof IsClientCacheable) {
final IsClientCacheable clientCacheableAction = (IsClientCacheable) action;
final String instanceKey = key(clientCacheableAction);
int cacheTotalTimeToLiveMillis = defaultTimeToLive;
if (result instanceof HasClientCacheTotalTimeToLive) {
cacheTotalTimeToLiveMillis = ((HasClientCacheTotalTimeToLive) result).cacheTotalTimeToLiveMillis();
} else if (action instanceof HasClientCacheTotalTimeToLive) {
cacheTotalTimeToLiveMillis = ((HasClientCacheTotalTimeToLive) action).cacheTotalTimeToLiveMillis();
}
resultsCache.put(instanceKey, new ResultHolder(System.currentTimeMillis()
+ cacheTotalTimeToLiveMillis, result));
LOG.finest("Added " + instanceKey + " to cache, ttl: " + cacheTotalTimeToLiveMillis + "ms");
if (invalidationTask != null && !invalidationTask.isRunning()) {
LOG.finest("Schedule cache cleanup");
invalidationTask.schedule((int) Duration.ONE_MINUTE.times(5).asMillis());
}
final IsClientCacheable clientCacheableAction = (IsClientCacheable) action;
final String instanceKey = key(clientCacheableAction);
int cacheTotalTimeToLiveMillis = timeToLive(action, result);
resultsCache.put(instanceKey, new ResultHolder(System.currentTimeMillis() + cacheTotalTimeToLiveMillis,
result));
LOG.finest("Added " + instanceKey + " to cache, ttl: " + cacheTotalTimeToLiveMillis + "ms");
if (invalidationTask != null && !invalidationTask.isRunning()) {
LOG.finest("Schedule cache cleanup");
invalidationTask.schedule((int) Duration.ONE_MINUTE.times(5).asMillis());
}
callback.onSuccess(result);
}
/**
* Calculate time to live fore result
*
* @param action
* @param result
* @return
*/
private int timeToLive(A action, R result) {
int cacheTotalTimeToLiveMillis = defaultTimeToLive;
if (result instanceof HasClientCacheTotalTimeToLive) {
cacheTotalTimeToLiveMillis = ((HasClientCacheTotalTimeToLive) result).cacheTotalTimeToLiveMillis();
} else if (action instanceof HasClientCacheTotalTimeToLive) {
cacheTotalTimeToLiveMillis = ((HasClientCacheTotalTimeToLive) action).cacheTotalTimeToLiveMillis();
}
return cacheTotalTimeToLiveMillis;
}
@Override
public void onFailure(Throwable caught) {
callback.onFailure(caught);
@@ -130,8 +154,6 @@ public final class CachingDispatch<CTX extends DispatchContext> implements Dispa
/**
* Holder class used to store results in cache.
*
* @author pgtaboada
*
*/
private static class ResultHolder {
final Result payload;
@@ -168,8 +190,6 @@ public final class CachingDispatch<CTX extends DispatchContext> implements Dispa
/**
* Removes due elements from resultscache.
*
* @author pgtaboada
*
*/
private class CacheCleanupTask extends Timer {
@Override
@@ -1,9 +1,7 @@
package com.sap.sse.gwt.dispatch.client.caching;
/**
* Interface used by actions and results to override default cache time to live.
*
* @author pgtaboada
* Interface used by actions and results to provide cache time to live.
*
*/
public interface HasClientCacheTotalTimeToLive {
@@ -3,13 +3,14 @@ package com.sap.sse.gwt.dispatch.client.caching;
/**
* Cache interface used by the caching dispatch to identify which command can be cached on client side.
*
* @author pgtaboada
* Each command must provide an instance key used to store the results in the cache.
*
* The instance key is appended to an class identifying key.
*/
public interface IsClientCacheable {
/**
* The instance specific key used to identify instance specific results in the cache.
* The builder used to identify instance specific results in the cache.
*
* @return
*/
@@ -1,16 +1,25 @@
package com.sap.sse.gwt.dispatch.client.exceptions;
import java.util.UUID;
import com.google.gwt.core.shared.GwtIncompatible;
import com.google.gwt.user.client.rpc.IsSerializable;
public class DispatchException extends RuntimeException implements IsSerializable {
private static final long serialVersionUID = 8192187255698006941L;
private String exceptionId;
protected DispatchException() {
}
@GwtIncompatible
public DispatchException(String message) {
super(message);
exceptionId = UUID.randomUUID().toString();
}
public String getExceptionId() {
return exceptionId;
}
}
@@ -1,28 +1,17 @@
package com.sap.sse.gwt.dispatch.client.exceptions;
import java.util.UUID;
import com.google.gwt.core.shared.GwtIncompatible;
public class ServerDispatchException extends DispatchException {
private static final long serialVersionUID = 8192187255698006941L;
private String uuid;
protected ServerDispatchException() {
}
public ServerDispatchException(String uuid, Throwable cause) {
super(cause.getMessage());
this.uuid = uuid;
}
@GwtIncompatible
public ServerDispatchException(Throwable cause) {
super(cause.getMessage());
this.uuid = UUID.randomUUID().toString();
}
public String getUuid() {
return uuid;
}
}
@@ -2,9 +2,15 @@ package com.sap.sse.gwt.dispatch.client.impl;
import java.util.Date;
import com.sap.sse.gwt.dispatch.client.DispatchAsync;
import com.sap.sse.gwt.dispatch.client.DispatchSystemAsync;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
public interface DispatchSystem<CTX extends DispatchContext> extends DispatchAsync<CTX> {
/**
* The dispatch system is the client side implementation of the dispatch communication pattern.
*
* @param <CTX>
* the context that will be provided to the actions during execution
*/
public interface DispatchSystem<CTX extends DispatchContext> extends DispatchSystemAsync<CTX> {
Date getCurrentServerTime();
}
@@ -1,43 +1,62 @@
package com.sap.sse.gwt.dispatch.client.impl;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchAsync;
import com.sap.sse.gwt.dispatch.client.DispatchSystemAsync;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.batching.AutomaticBatchingDispatch;
import com.sap.sse.gwt.dispatch.client.caching.CachingDispatch;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
import com.sap.sse.gwt.dispatch.client.exceptions.ServerDispatchException;
import com.sap.sse.gwt.dispatch.client.rpcimpl.SimpleDispatch;
public class DispatchSystemImpl<CTX extends DispatchContext> implements DispatchSystem<CTX> {
/**
* Base implementation of a client side dispatch executor.
*
* The dispatch system implements caching {@link CachingDispatch} and automatic batching
* {@link AutomaticBatchingDispatch}.
*
* @param <CTX>
*/
public abstract class DispatchSystemImpl<CTX extends DispatchContext> implements DispatchSystem<CTX> {
private final Logger LOG = Logger.getLogger(DispatchSystemImpl.class.getName());
private final SimpleDispatch<CTX> simpleDispatch;
private final DispatchAsync<CTX> dispatch;
private final DispatchSystemAsync<CTX> dispatch;
public DispatchSystemImpl(String dispatchRPCPath) {
this(dispatchRPCPath, false);
}
/**
*
* Create a dispatch system
*
* @param dispatchRPCPath
* rpc-servlet path
* @param processResultsScheduled
* use a {@link Scheduler} to process the results
*/
public DispatchSystemImpl(String dispatchRPCPath, boolean processResultsScheduled) {
simpleDispatch = new SimpleDispatch<CTX>(dispatchRPCPath);
dispatch = new CachingDispatch<CTX>(new AutomaticBatchingDispatch<CTX>(
simpleDispatch, processResultsScheduled));
LOG.finest("Started dispatch system for " + dispatchRPCPath);
}
@Override
public <R extends Result, A extends Action<R, CTX>> void execute(final A action, final AsyncCallback<R> callback) {
// TODO: client side execution time logging
AsyncCallback<R> wrappedCallback = new AsyncCallback<R>() {
final AsyncCallback<R> wrappedCallback = new AsyncCallback<R>() {
@Override
public void onFailure(Throwable caught) {
if (caught instanceof ServerDispatchException) {
if (caught instanceof DispatchException) {
ServerDispatchException sde = (ServerDispatchException) caught;
// TODO: browser console log
GWT.log("Server exception with id: " + sde.getUuid());
LOG.log(Level.SEVERE, "Server exception with id: " + sde.getExceptionId());
}
callback.onFailure(caught);
}
@@ -4,9 +4,13 @@ import com.google.gwt.user.client.rpc.RemoteService;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.ResultWrapper;
import com.sap.sse.gwt.dispatch.client.exceptions.DispatchException;
/**
* GWT RPC interfaces for dispatch communication
*
* @param <CTX>
*/
public interface DispatchRPC<CTX extends DispatchContext> extends RemoteService {
<R extends Result, A extends Action<R, CTX>> ResultWrapper<R> execute(
@@ -4,8 +4,12 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.ResultWrapper;
/**
* Async interface
*
* @param <CTX>
*/
public interface DispatchRPCAsync<CTX extends DispatchContext> {
<R extends Result, A extends Action<R, CTX>> void execute(
@@ -7,6 +7,16 @@ import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
/**
* Wrapper that encapsulates an action to be sent to the server.
*
* @param <A>
* Action
* @param <R>
* Result
* @param <CTX>
* Execution context
*/
public class RequestWrapper<R extends Result, A extends Action<R, CTX>, CTX extends DispatchContext> implements
IsSerializable {
@@ -1,9 +1,15 @@
package com.sap.sse.gwt.dispatch.client;
package com.sap.sse.gwt.dispatch.client.rpcimpl;
import java.util.Date;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sse.gwt.dispatch.client.Result;
/**
* Result wrapper that encapsulates the curent server time.
*
* @param <R>
*/
public class ResultWrapper<R extends Result> implements IsSerializable {
private Date currentServerTime = new Date();
@@ -5,12 +5,16 @@ import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.sap.sse.gwt.dispatch.client.Action;
import com.sap.sse.gwt.dispatch.client.DispatchAsync;
import com.sap.sse.gwt.dispatch.client.DispatchSystemAsync;
import com.sap.sse.gwt.dispatch.client.DispatchContext;
import com.sap.sse.gwt.dispatch.client.Result;
import com.sap.sse.gwt.dispatch.client.ResultWrapper;
public class SimpleDispatch<CTX extends DispatchContext> implements DispatchAsync<CTX> {
/**
* Simple dispatch implementation that uses GWT RPC internally to communicate with the server
*
* @param <CTX>
*/
public class SimpleDispatch<CTX extends DispatchContext> implements DispatchSystemAsync<CTX> {
private final DispatchRPCAsync<CTX> dispatchRPC = GWT.create(DispatchRPC.class);
@@ -22,7 +26,8 @@ public class SimpleDispatch<CTX extends DispatchContext> implements DispatchAsyn
@Override
public <R extends Result, A extends Action<R, CTX>> void execute(A action, final AsyncCallback<R> callback) {
RequestWrapper<R, A, CTX> requestWrapper = new RequestWrapper<R, A, CTX>(action, LocaleInfo.getCurrentLocale()
final RequestWrapper<R, A, CTX> requestWrapper = new RequestWrapper<R, A, CTX>(action, LocaleInfo
.getCurrentLocale()
.getLocaleName());
final long clientTimeOnRequestStart = System.currentTimeMillis();
dispatchRPC.execute(requestWrapper, new AsyncCallback<ResultWrapper<R>>() {