diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/DashboardDispatchRPCServletImpl.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/DashboardDispatchRPCServletImpl.java index 845b1dcfb80..4e992218432 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/DashboardDispatchRPCServletImpl.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/DashboardDispatchRPCServletImpl.java @@ -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 { @@ -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; diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/home/shared/dispatch/CacheDispatchTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/home/shared/dispatch/CacheDispatchTest.java index ddbd6b0d13b..63b53bfacac 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/home/shared/dispatch/CacheDispatchTest.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/home/shared/dispatch/CacheDispatchTest.java @@ -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 { + private class DispatchMock implements DispatchSystemAsync { @Override public > void execute(A action, AsyncCallback callback) throws DispatchException { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/DispatchRPCServletImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/DispatchRPCServletImpl.java index 69d15784fc4..e9f4778a01f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/DispatchRPCServletImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/DispatchRPCServletImpl.java @@ -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 { @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java index 05865420423..84aec3f51f1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java @@ -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; } } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Action.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Action.java index 2a4d955f9d1..cb92d9e4b40 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Action.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Action.java @@ -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 + * @param + */ public interface Action 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; diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/CollectionResult.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/CollectionResult.java index 1b7e711f327..3c15fad3138 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/CollectionResult.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/CollectionResult.java @@ -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 + */ public interface CollectionResult extends DTO, Result { Collection getValues(); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/DispatchAsync.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/DispatchAsync.java deleted file mode 100644 index 477a63cd4d2..00000000000 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/DispatchAsync.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.sap.sse.gwt.dispatch.client; - -import com.google.gwt.user.client.rpc.AsyncCallback; - -public interface DispatchAsync { - - > void execute(A action, AsyncCallback callback); - -} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/DispatchSystemAsync.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/DispatchSystemAsync.java new file mode 100644 index 00000000000..6ddb49d1f8d --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/DispatchSystemAsync.java @@ -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 + */ +public interface DispatchSystemAsync { + + /** + * 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. + */ + > void execute(A action, AsyncCallback callback); + +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Result.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Result.java index 229d222e099..738e6b81e6c 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Result.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/Result.java @@ -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 { } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWithTTL.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWithTTL.java index 31b35316d09..975592414f1 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWithTTL.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWithTTL.java @@ -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 + */ public class ResultWithTTL implements Result, HasClientCacheTotalTimeToLive { /** max millis the result might be loaded earlier so we do batches **/ diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/AutomaticBatchingDispatch.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/AutomaticBatchingDispatch.java index 31a973a8adb..0708d9f0ca3 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/AutomaticBatchingDispatch.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/AutomaticBatchingDispatch.java @@ -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 implements DispatchAsync { +public class AutomaticBatchingDispatch implements DispatchSystemAsync { private static final Logger LOG = Logger.getLogger(AutomaticBatchingDispatch.class.getName()); - private final DispatchAsync executionContext; + private final DispatchSystemAsync executionContext; private DispatchCallStack currentCallstack; @@ -29,7 +29,7 @@ public class AutomaticBatchingDispatch implements D * the underlying service implementation to use * */ - public AutomaticBatchingDispatch(DispatchAsync service) { + public AutomaticBatchingDispatch(DispatchSystemAsync service) { this(service, false); } @@ -40,9 +40,10 @@ public class AutomaticBatchingDispatch implements D * * @param service * the underlying service implementation to use - * + * @param processResultsScheduled + * use a {@link Scheduler} to process the results */ - public AutomaticBatchingDispatch(DispatchAsync service, boolean processResultsScheduled) { + public AutomaticBatchingDispatch(DispatchSystemAsync service, boolean processResultsScheduled) { this.executionContext = service; this.processResultsScheduled = processResultsScheduled; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchAction.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchAction.java index bae7351d45a..0c0c7f683aa 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchAction.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchAction.java @@ -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 implements Action { +public final class BatchAction implements Action { private static final Logger logger = Logger.getLogger(BatchAction.class.getName()); - private Action[] actions; + private ArrayList> actions = new ArrayList>(); - @SuppressWarnings("unused") - private BatchAction() { + protected BatchAction() { } - @SafeVarargs - public BatchAction(Action... actions) { - this.actions = actions; + public BatchAction(ArrayList> actions) { + if (actions != null) { + this.actions.addAll(actions); + } } - public Action[] getActions() { + public ArrayList> getActions() { return actions; } @@ -31,7 +31,7 @@ public class BatchAction implements Action results = new ArrayList(nrOfActions); final ArrayList exceptions = new ArrayList(nrOfActions); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchResult.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchResult.java index aacd598ac95..6bdad089199 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchResult.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/BatchResult.java @@ -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 results = new LinkedList(); +public final class BatchResult implements Result { + private ArrayList results = new ArrayList<>(); + private ArrayList exceptions = new ArrayList<>(); - private LinkedList exceptions = new LinkedList(); - - @SuppressWarnings("unused") - private BatchResult() { + protected BatchResult() { } + @GwtIncompatible public BatchResult(List results, List 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 getResult(int i, Class type) { Object result = results.get(i); + if (result != null && type.getName().equals(result.getClass().getName())) return (T) result; return null; diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/DispatchCallStack.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/DispatchCallStack.java index 720832a1232..41152a158ea 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/DispatchCallStack.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/DispatchCallStack.java @@ -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 { /** * The list containg all queued actions. */ - private final LinkedList> actionQueue = new LinkedList>(); + private final ArrayList> actionQueue = new ArrayList>(); /** * 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 { /** * 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 , R extends Result> void addCall(A action, AsyncCallback callback) { assert (this.batchAction == null); @@ -38,16 +39,15 @@ public final class DispatchCallStack { } /** - * 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 getBatchAction() { if (this.batchAction == null) { - @SuppressWarnings("unchecked") - Action[] actions = new Action[this.actionQueue.size()]; - for (int loop = 0; loop < actions.length; loop++) { - actions[loop] = this.actionQueue.get(loop).getAction(); + ArrayList> actions = new ArrayList>(); + for (DispatchCall dispatchCall : this.actionQueue) { + actions.add(dispatchCall.getAction()); } this.batchAction = new BatchAction(actions); } @@ -67,16 +67,16 @@ public final class DispatchCallStack { } /** - * 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 call : this.actionQueue) { Result result = batchResult.getResult(loop); Throwable exception = batchResult.getException(loop); @@ -90,17 +90,24 @@ public final class DispatchCallStack { } } - @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()) { diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/NonBatchableAction.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/NonBatchableAction.java index abea374ea8c..4af4b24a17e 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/NonBatchableAction.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/NonBatchableAction.java @@ -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 { } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/SplitScheduler.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/SplitScheduler.java index a64a4073851..e19424b3c44 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/SplitScheduler.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/batching/SplitScheduler.java @@ -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); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/CachingDispatch.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/CachingDispatch.java index 3673c5eabc0..84cfa625b82 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/CachingDispatch.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/CachingDispatch.java @@ -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 implements DispatchAsync { - +public final class CachingDispatch implements DispatchSystemAsync { private static final Logger LOG = Logger.getLogger(CachingDispatch.class.getName()); - private final CacheCleanupTask invalidationTask; private final HashMap resultsCache = new HashMap<>(); - private final DispatchAsync dispatch; + private final DispatchSystemAsync dispatch; private final int defaultTimeToLive; - public CachingDispatch(DispatchAsync service) { + /** + * Create new caching dispatch instance with default values. + * + * @param service + * the underlying service used to make dispatch calls + */ + public CachingDispatch(DispatchSystemAsync service) { this(service, true, (int) Duration.ONE_MINUTE.times(3).asMillis()); } - public CachingDispatch(DispatchAsync 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 service, boolean enableCleanup, int defaultTimeToLive) { this.dispatch = service; this.defaultTimeToLive = defaultTimeToLive; if (enableCleanup) { @@ -60,21 +73,23 @@ public final class CachingDispatch 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 > void executeAndCache(final A action, final AsyncCallback 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 * @param */ @@ -89,26 +104,35 @@ public final class CachingDispatch 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 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 implements Dispa /** * Removes due elements from resultscache. * - * @author pgtaboada - * */ private class CacheCleanupTask extends Timer { @Override diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/HasClientCacheTotalTimeToLive.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/HasClientCacheTotalTimeToLive.java index f6b88528041..75b402ecca7 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/HasClientCacheTotalTimeToLive.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/HasClientCacheTotalTimeToLive.java @@ -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 { diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/IsClientCacheable.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/IsClientCacheable.java index 6614d218a94..c1d6341048a 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/IsClientCacheable.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/caching/IsClientCacheable.java @@ -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 */ diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/DispatchException.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/DispatchException.java index cd86ad240a8..b314c3bae24 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/DispatchException.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/DispatchException.java @@ -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; } } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/ServerDispatchException.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/ServerDispatchException.java index 8c509aba789..7622aa41bbd 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/ServerDispatchException.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/exceptions/ServerDispatchException.java @@ -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; } } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystem.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystem.java index dc3f8175ad1..cba2b45bb63 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystem.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystem.java @@ -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 extends DispatchAsync { +/** + * The dispatch system is the client side implementation of the dispatch communication pattern. + * + * @param + * the context that will be provided to the actions during execution + */ +public interface DispatchSystem extends DispatchSystemAsync { Date getCurrentServerTime(); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystemImpl.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystemImpl.java index 79000d31e64..ac6d91f6a34 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystemImpl.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/impl/DispatchSystemImpl.java @@ -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 implements DispatchSystem { - +/** + * Base implementation of a client side dispatch executor. + * + * The dispatch system implements caching {@link CachingDispatch} and automatic batching + * {@link AutomaticBatchingDispatch}. + * + * @param + */ +public abstract class DispatchSystemImpl implements DispatchSystem { + private final Logger LOG = Logger.getLogger(DispatchSystemImpl.class.getName()); private final SimpleDispatch simpleDispatch; - private final DispatchAsync dispatch; + private final DispatchSystemAsync 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(dispatchRPCPath); dispatch = new CachingDispatch(new AutomaticBatchingDispatch( simpleDispatch, processResultsScheduled)); + LOG.finest("Started dispatch system for " + dispatchRPCPath); } @Override public > void execute(final A action, final AsyncCallback callback) { - // TODO: client side execution time logging - AsyncCallback wrappedCallback = new AsyncCallback() { + final AsyncCallback wrappedCallback = new AsyncCallback() { @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); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPC.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPC.java index 2e828fad41e..464e6a4814f 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPC.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPC.java @@ -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 + */ public interface DispatchRPC extends RemoteService { > ResultWrapper execute( diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPCAsync.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPCAsync.java index 0d0c21254a5..9656ef9cc0b 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPCAsync.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/DispatchRPCAsync.java @@ -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 + */ public interface DispatchRPCAsync { > void execute( diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/RequestWrapper.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/RequestWrapper.java index 504507275bd..bfbf8fdb831 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/RequestWrapper.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/RequestWrapper.java @@ -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 + * Action + * @param + * Result + * @param + * Execution context + */ public class RequestWrapper, CTX extends DispatchContext> implements IsSerializable { diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWrapper.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/ResultWrapper.java similarity index 75% rename from java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWrapper.java rename to java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/ResultWrapper.java index f81121da193..be3644c59e1 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/ResultWrapper.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/ResultWrapper.java @@ -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 + */ public class ResultWrapper implements IsSerializable { private Date currentServerTime = new Date(); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/SimpleDispatch.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/SimpleDispatch.java index b800e474fc0..b5b0db4440f 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/SimpleDispatch.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/rpcimpl/SimpleDispatch.java @@ -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 implements DispatchAsync { +/** + * Simple dispatch implementation that uses GWT RPC internally to communicate with the server + * + * @param + */ +public class SimpleDispatch implements DispatchSystemAsync { private final DispatchRPCAsync dispatchRPC = GWT.create(DispatchRPC.class); @@ -22,7 +26,8 @@ public class SimpleDispatch implements DispatchAsyn @Override public > void execute(A action, final AsyncCallback callback) { - RequestWrapper requestWrapper = new RequestWrapper(action, LocaleInfo.getCurrentLocale() + final RequestWrapper requestWrapper = new RequestWrapper(action, LocaleInfo + .getCurrentLocale() .getLocaleName()); final long clientTimeOnRequestStart = System.currentTimeMillis(); dispatchRPC.execute(requestWrapper, new AsyncCallback>() {