mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-28 14:57:12 +00:00
Added JavaDocs to RefreshManager and subclasses as well as ResultWithTTL
This commit is contained in:
1 parent
62365bf9cc
commit
340eb2a299
5 files changed
+108
-3
No files matched your search
+6
@@ -8,6 +8,12 @@ import com.google.gwt.user.client.ui.AcceptsOneWidget;
|
|||||||
import com.google.gwt.user.client.ui.Widget;
|
import com.google.gwt.user.client.ui.Widget;
|
||||||
import com.sap.sailing.gwt.home.communication.SailingDispatchSystem;
|
import com.sap.sailing.gwt.home.communication.SailingDispatchSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized {@link LifecycleRefreshManager} that is bound to the lifecycle of a given {@link AcceptsOneWidget} and
|
||||||
|
* ensures that a content {@link Widget} is being attached when the first refresh is successfully finished. This enures
|
||||||
|
* that no uninitialized {@link RefreshableWidget} are initially shown. Instead you can explicitly set a placeholder
|
||||||
|
* widget to the container that is automatically replaced with the actual content when the data is available.
|
||||||
|
*/
|
||||||
public class AutoAttachingRefreshManager extends LifecycleRefreshManager {
|
public class AutoAttachingRefreshManager extends LifecycleRefreshManager {
|
||||||
private static final Logger LOG = Logger.getLogger(AutoAttachingRefreshManager.class.getName());
|
private static final Logger LOG = Logger.getLogger(AutoAttachingRefreshManager.class.getName());
|
||||||
|
|
||||||
|
|||||||
+19
@@ -7,9 +7,23 @@ import com.google.gwt.event.logical.shared.AttachEvent.Handler;
|
|||||||
import com.google.gwt.event.logical.shared.HasAttachHandlers;
|
import com.google.gwt.event.logical.shared.HasAttachHandlers;
|
||||||
import com.sap.sailing.gwt.home.communication.SailingDispatchSystem;
|
import com.sap.sailing.gwt.home.communication.SailingDispatchSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized {@link RefreshManager} that is being bound to the lifecycle of a given widget. This is typically a
|
||||||
|
* container that holds the layout of the inner refreshable parts.
|
||||||
|
*
|
||||||
|
* This should always be used when refresh functionality is used by a UI part that can disappear sometime. This is e.g
|
||||||
|
* the case for Activities that are replaced by another Activity when a new Place fires.
|
||||||
|
*
|
||||||
|
* The implementation ensures that no memory leak is produced by the inner Timer instance by cleanly stopping all work
|
||||||
|
* when the lifecycle widget is detached from the DOM tree.
|
||||||
|
*/
|
||||||
public class LifecycleRefreshManager extends RefreshManager {
|
public class LifecycleRefreshManager extends RefreshManager {
|
||||||
private final HasAttachHandlers lifecycleWidget;
|
private final HasAttachHandlers lifecycleWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lifecycleWidget the widget that defines the lifecycle of this {@link RefreshManager}.
|
||||||
|
* @param actionExecutor the {@link SailingDispatchSystem} to use for remote communication
|
||||||
|
*/
|
||||||
public LifecycleRefreshManager(HasAttachHandlers lifecycleWidget, SailingDispatchSystem actionExecutor) {
|
public LifecycleRefreshManager(HasAttachHandlers lifecycleWidget, SailingDispatchSystem actionExecutor) {
|
||||||
super(actionExecutor);
|
super(actionExecutor);
|
||||||
this.lifecycleWidget = lifecycleWidget;
|
this.lifecycleWidget = lifecycleWidget;
|
||||||
@@ -24,6 +38,9 @@ public class LifecycleRefreshManager extends RefreshManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if the widget is already attached, the listener above won't fire, so we trigger the first refresh directly.
|
||||||
|
// scheduleFinally is used to wait until all RefreshableWidgets are added.
|
||||||
|
// Otherwise, there would be no RefreshableWidget and the initial refresh would be skipped, so that not refresh would occur at all.
|
||||||
Scheduler.get().scheduleFinally(new ScheduledCommand() {
|
Scheduler.get().scheduleFinally(new ScheduledCommand() {
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
@@ -36,6 +53,8 @@ public class LifecycleRefreshManager extends RefreshManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canExecute() {
|
protected boolean canExecute() {
|
||||||
|
// Refreshes can only occur if the lifecycleWidget is actually attached to the DOM tree.
|
||||||
|
// Other wise we would produce a memory consuming zombie that eats the browser's resources and unnecessarily fetches data from the server.
|
||||||
return lifecycleWidget.isAttached();
|
return lifecycleWidget.isAttached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+75
-1
@@ -16,9 +16,25 @@ import com.sap.sse.common.Duration;
|
|||||||
import com.sap.sse.gwt.dispatch.shared.commands.DTO;
|
import com.sap.sse.gwt.dispatch.shared.commands.DTO;
|
||||||
import com.sap.sse.gwt.dispatch.shared.commands.ResultWithTTL;
|
import com.sap.sse.gwt.dispatch.shared.commands.ResultWithTTL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides automatic refresh functionality for widgets based on the {@link SailingDispatchSystem}.
|
||||||
|
*
|
||||||
|
* A widget can be registered for automatic refresh if it implements {@link RefreshableWidget} and it's data is provided
|
||||||
|
* by a dispatch action of type {@link SailingAction}. This action needs to have it's result wrapped by a
|
||||||
|
* {@link ResultWithTTL} to control refresh intervals by the server.
|
||||||
|
*
|
||||||
|
* The {@link RefreshManager} ensures that the next refresh will occur in about the time defined by the returned
|
||||||
|
* {@link ResultWithTTL}. While doing this it tries to batch refreshes that are scheduled for a similar time point.
|
||||||
|
*
|
||||||
|
* If an error occurs while loading new data on a refresh, the next refresh is automatically scheduled in 30 seconds to
|
||||||
|
* not kill the server by immediate repetitive remote calls.
|
||||||
|
*/
|
||||||
public abstract class RefreshManager {
|
public abstract class RefreshManager {
|
||||||
private static final Logger LOG = Logger.getLogger(RefreshManager.class.getName());
|
private static final Logger LOG = Logger.getLogger(RefreshManager.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an error occurs, the next refresh is delayed by 30 seconds to not kill the server by doing immediate repetitive calls.
|
||||||
|
*/
|
||||||
private static final long PAUSE_ON_ERROR = Duration.ONE_SECOND.times(30).asMillis();
|
private static final long PAUSE_ON_ERROR = Duration.ONE_SECOND.times(30).asMillis();
|
||||||
private List<RefreshHolder<DTO, SailingAction<ResultWithTTL<DTO>>>> refreshables = new ArrayList<>();
|
private List<RefreshHolder<DTO, SailingAction<ResultWithTTL<DTO>>>> refreshables = new ArrayList<>();
|
||||||
|
|
||||||
@@ -34,30 +50,53 @@ public abstract class RefreshManager {
|
|||||||
|
|
||||||
boolean started = false;
|
boolean started = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param actionExecutor the {@link SailingDispatchSystem} to use for remote communication
|
||||||
|
*/
|
||||||
public RefreshManager(SailingDispatchSystem actionExecutor) {
|
public RefreshManager(SailingDispatchSystem actionExecutor) {
|
||||||
this.actionExecutor = actionExecutor;
|
this.actionExecutor = actionExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initially starts the auto-refresh. Needs to be called once by sublasses when at least one {@link RefreshableWidget} is added.
|
||||||
|
* This triggers the first refresh. Subsequent refreshes are automatically triggered until {@link #cancel()} is called.
|
||||||
|
*/
|
||||||
protected void start() {
|
protected void start() {
|
||||||
LOG.log(Level.FINE, "Starting auto refresh");
|
LOG.log(Level.FINE, "Starting auto refresh");
|
||||||
started = true;
|
started = true;
|
||||||
reschedule();
|
reschedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be called by subclasses to stop the auto-refresh. Ensures that all resources are disposed that would
|
||||||
|
* otherwise lead to memory leaks or zombie processes.
|
||||||
|
*/
|
||||||
protected void cancel() {
|
protected void cancel() {
|
||||||
LOG.log(Level.FINE, "Cancelling auto refresh");
|
LOG.log(Level.FINE, "Cancelling auto refresh");
|
||||||
started = false;
|
started = false;
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if a refresh may occur in the current state, false otherwise.
|
||||||
|
*/
|
||||||
protected abstract boolean canExecute();
|
protected abstract boolean canExecute();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is called whenever a refresh was successful. Useful for subclasses that need to hook into the lifecycle of the {@link RefreshManager}.
|
||||||
|
*/
|
||||||
protected void onSuccessfulUpdate() {
|
protected void onSuccessfulUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is called whenever a refresh failed. Useful for subclasses that need to hook into the lifecycle of the {@link RefreshManager}.
|
||||||
|
*/
|
||||||
protected void onFailedUpdate(Throwable errorCause) {
|
protected void onFailedUpdate(Throwable errorCause) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers an update for all {@link RefreshableWidget}s that will have a timeout in the next 5 seconds.
|
||||||
|
*/
|
||||||
private void update() {
|
private void update() {
|
||||||
|
|
||||||
for (final RefreshHolder<DTO, SailingAction<ResultWithTTL<DTO>>> refreshable : refreshables) {
|
for (final RefreshHolder<DTO, SailingAction<ResultWithTTL<DTO>>> refreshable : refreshables) {
|
||||||
@@ -94,6 +133,9 @@ public abstract class RefreshManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the next update is scheduled by a timer.
|
||||||
|
*/
|
||||||
private void reschedule() {
|
private void reschedule() {
|
||||||
if (scheduled) {
|
if (scheduled) {
|
||||||
return;
|
return;
|
||||||
@@ -129,6 +171,7 @@ public abstract class RefreshManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(nextUpdate == null) {
|
if(nextUpdate == null) {
|
||||||
|
// This can occur, if there is already a call running for all RefreshableWidgets
|
||||||
LOG.log(Level.FINE, "Nothing to auto update");
|
LOG.log(Level.FINE, "Nothing to auto update");
|
||||||
} else {
|
} else {
|
||||||
int delayMillis = (int) (nextUpdate - System.currentTimeMillis());
|
int delayMillis = (int) (nextUpdate - System.currentTimeMillis());
|
||||||
@@ -151,6 +194,16 @@ public abstract class RefreshManager {
|
|||||||
reschedule();
|
reschedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a {@link RefreshableWidget} to the set of refreshables controlled by this {@link RefreshManager}.
|
||||||
|
* The refresh intervals are controlled by the ResultWithTTL returned from the server.
|
||||||
|
*
|
||||||
|
* On each refresh, the given {@link ActionProvider} is called to get the action to send to the server.
|
||||||
|
* This is useful if some state-dependent data needs to be sent so that the action can't be a fix instance.
|
||||||
|
*
|
||||||
|
* @param widget the {@link RefreshableWidget} to start auto refresh for
|
||||||
|
* @param provider the {@link ActionProvider} to get the {@link SailingAction} instance from when fetching new data for a refresh
|
||||||
|
*/
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
public <D extends DTO, A extends SailingAction<ResultWithTTL<D>>> void add(RefreshableWidget<? super D> widget,
|
public <D extends DTO, A extends SailingAction<ResultWithTTL<D>>> void add(RefreshableWidget<? super D> widget,
|
||||||
ActionProvider<A> provider) {
|
ActionProvider<A> provider) {
|
||||||
@@ -158,6 +211,15 @@ public abstract class RefreshManager {
|
|||||||
reschedule();
|
reschedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a {@link RefreshableWidget} to the set of refreshables controlled by this {@link RefreshManager}.
|
||||||
|
* The refresh intervals are controlled by the ResultWithTTL returned from the server.
|
||||||
|
*
|
||||||
|
* On each refresh, the given action is sent to the server to get new data.
|
||||||
|
*
|
||||||
|
* @param widget the {@link RefreshableWidget} to start auto refresh for
|
||||||
|
* @param action the {@link SailingAction} instance to use when fetching new data for a refresh
|
||||||
|
*/
|
||||||
public <D extends DTO, A extends SailingAction<ResultWithTTL<D>>> void add(RefreshableWidget<? super D> widget, A action) {
|
public <D extends DTO, A extends SailingAction<ResultWithTTL<D>>> void add(RefreshableWidget<? super D> widget, A action) {
|
||||||
add(widget, new DefaultActionProvider<>(action));
|
add(widget, new DefaultActionProvider<>(action));
|
||||||
}
|
}
|
||||||
@@ -166,12 +228,24 @@ public abstract class RefreshManager {
|
|||||||
return actionExecutor;
|
return actionExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal structure for holding the state for a {@link RefreshableWidget} controlled by the {@link RefreshManager}.
|
||||||
|
*
|
||||||
|
* @param <D> The Type of the Data returned from the server (wrapped by a {@link ResultWithTTL})
|
||||||
|
* @param <A> The type of the dispatch action for fetching new data
|
||||||
|
*/
|
||||||
private static class RefreshHolder<D extends DTO, A extends SailingAction<ResultWithTTL<D>>> {
|
private static class RefreshHolder<D extends DTO, A extends SailingAction<ResultWithTTL<D>>> {
|
||||||
private final RefreshableWidget<D> widget;
|
private final RefreshableWidget<D> widget;
|
||||||
private final ActionProvider<A> provider;
|
private final ActionProvider<A> provider;
|
||||||
|
|
||||||
// initial update is now
|
/**
|
||||||
|
* Defines the timeout when the next refresh will occur.
|
||||||
|
* Initial update is now to trigger an immediate update.
|
||||||
|
*/
|
||||||
private long timeout = System.currentTimeMillis();
|
private long timeout = System.currentTimeMillis();
|
||||||
|
/**
|
||||||
|
* When there is currently a call running for refreshing this {@link RefreshableWidget}, callRunning is set to true.
|
||||||
|
*/
|
||||||
private boolean callRunning = false;
|
private boolean callRunning = false;
|
||||||
|
|
||||||
public RefreshHolder(RefreshableWidget<D> widget, ActionProvider<A> provider) {
|
public RefreshHolder(RefreshableWidget<D> widget, ActionProvider<A> provider) {
|
||||||
|
|||||||
+6
@@ -6,6 +6,12 @@ import com.sap.sailing.gwt.home.communication.SailingDispatchSystem;
|
|||||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||||
import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory;
|
import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized {@link AutoAttachingRefreshManager} that initially shows a busy widget that is replayced with the actual
|
||||||
|
* content when the first refresh is finished. In the case of an error while doing the first refresh, an error widget is
|
||||||
|
* shown. When an error occurs on a further refresh, the old content simply isn't exchanged an a refresh is triggered as
|
||||||
|
* described for {@link RefreshManager}.
|
||||||
|
*/
|
||||||
public class RefreshManagerWithErrorAndBusy extends AutoAttachingRefreshManager {
|
public class RefreshManagerWithErrorAndBusy extends AutoAttachingRefreshManager {
|
||||||
private final AcceptsOneWidget container;
|
private final AcceptsOneWidget container;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -7,14 +7,14 @@ import com.sap.sse.gwt.dispatch.shared.caching.HasClientCacheTotalTimeToLive;
|
|||||||
/**
|
/**
|
||||||
* Result implementation that holds a {@link DTO} with a defined time to live expiration.
|
* Result implementation that holds a {@link DTO} with a defined time to live expiration.
|
||||||
*
|
*
|
||||||
* @param <T>
|
* @param <T> The type of the actual result that is wrapped by this.
|
||||||
*/
|
*/
|
||||||
public class ResultWithTTL<T extends DTO> implements Result, HasClientCacheTotalTimeToLive {
|
public class ResultWithTTL<T extends DTO> implements Result, HasClientCacheTotalTimeToLive {
|
||||||
|
|
||||||
/** max millis the result might be loaded earlier so we do batches **/
|
/** max millis the result might be loaded earlier so we do batches **/
|
||||||
public static final Duration MAX_TIME_TO_LOAD_EARLIER = Duration.ONE_SECOND.times(5);
|
public static final Duration MAX_TIME_TO_LOAD_EARLIER = Duration.ONE_SECOND.times(5);
|
||||||
|
|
||||||
/** Time to live*/
|
/** Time to live. Defines when the client should ask for data the next time.*/
|
||||||
private Duration ttl;
|
private Duration ttl;
|
||||||
private T dto;
|
private T dto;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
|||||||
Reference in new issue
Block a user