[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-openjfx-dev
Subject:    Re: [API-REVIEW] RT-18702: Auto-restartable ScheduledService implementation
From:       Scott Palmer <swpalmer () gmail ! com>
Date:       2013-01-30 0:36:39
Message-ID: C8D6B6AE-DEE5-402F-B037-14C3764A2116 () gmail ! com
[Download RAW message or body]

That's much clearer, thanks.

Scott

On 2013-01-29, at 7:32 PM, Richard Bair <richard.bair@oracle.com> wrote:

> Thanks Scott. How about:
> 
> * <p>Once RUNNING, the ScheduledService will execute its Task. On successful
> * completion, the ScheduledService will transition to the SUCCEEDED state,
> * and then to the READY state and back to the SCHEDULED state. The amount
> * of time the ScheduledService will remain in this state depends on the
> * amount of time between the last state transition to RUNNING, and the
> * current time, and the <code>period</code>. In short, the <code>period</code>
> * defines the minimum amount of time from the start of one run and the start of
> * the next. If the previous execution completed before <code>period</code> expires,
> * then the ScheduledService will remain in the SCHEDULED state until the period
> * expires. If on the other hand the execution took longer than the
> * specified period, then the ScheduledService will immediately transition
> * back to RUNNING. </p>
> 
> On Jan 29, 2013, at 4:05 PM, Scott Palmer <swpalmer@gmail.com> wrote:
> 
> > Just a comment on the documentation.  I find the phrase "the minimum amount of \
> > time between executions." to conflict with the text that follows: 
> > "If the previous execution completed before <code>period</code> expires, then the \
> > ScheduledService will remain in the SCHEDULED state until the period expires." 
> > It appears that this is a treated as the interval between the start of one run \
> > and the start of the next run,  not the delay between when one run ends and the \
> > next run begins.  Correct?  If so "minimum time between executions" can be \
> > confusing, as I think of it as the delay between the end of the previous run and \
> > the start of the next. 
> > Scott
> > 
> > On 2013-01-29, at 6:02 PM, richardbair@me.com wrote:
> > 
> > > This has been a long time in coming! This is a followup to the original API \
> > > design email back in 2011. I've finished the implementation & testing and will \
> > > be pushing it shortly. I wanted to update this thread with the latest API \
> > > (which is almost exactly as specified back in 2011) in case anybody would like \
> > > to give a review. 
> > > Randahl, you might want to look at the names particularly of the \
> > > "backoffStrategy" and see if I got the capitalization right :-). I wasn't sure \
> > > if it should be back off or backoff. 
> > > Also, anyone with genius math skills can let me know if I implemented / spec'd \
> > > the different backoff strategies correctly. 
> > > Thanks
> > > Richard
> > > 
> > > /**
> > > * <p>The ScheduledService is a {@link Service} which will automatically restart
> > > * itself after a successful execution, and under some conditions will
> > > * restart even in case of failure. A new ScheduledService begins in
> > > * the READY state, just as a normal Service. After calling
> > > * <code>start</code> or <code>restart</code>, the ScheduledService will
> > > * enter the SCHEDULED state for the duration specified by <code>delay</code>.
> > > * </p>
> > > *
> > > * <p>Once RUNNING, the ScheduledService will execute its Task. On successful
> > > * completion, the ScheduledService will transition to the SUCCEEDED state,
> > > * and then to the READY state and back to the SCHEDULED state. The amount
> > > * of time the ScheduledService will remain in this state depends on the
> > > * amount of time between the last state transition to RUNNING, and the
> > > * current time, and the <code>period</code>. In short, the <code>period</code>
> > > * defines the minimum amount of time between executions. If the previous
> > > * execution completed before <code>period</code> expires, then the
> > > * ScheduledService will remain in the SCHEDULED state until the period
> > > * expires. If on the other hand the execution took longer than the
> > > * specified period, then the ScheduledService will immediately transition
> > > * back to RUNNING. </p>
> > > *
> > > * <p>If, while RUNNING, the ScheduledService's Task throws an error or in
> > > * some other way ends up transitioning to FAILED, then the ScheduledService
> > > * will either restart or quit, depending on the values for
> > > * <code>backoffStrategy</code>, <code>restartOnFailure</code>, and
> > > * <code>maximumFailureCount</code>.</p>
> > > *
> > > * <p>If a failure occurs and <code>restartOnFailure</code> is false, then
> > > * the ScheduledService will transition to FAILED and will stop. To restart
> > > * a failed ScheduledService, you must call restart manually.</p>
> > > *
> > > * <p>If a failure occurs and <code>restartOnFailure</code> is true, then
> > > * the the ScheduledService <em>may</em> restart automatically. First,
> > > * the result of calling <code>backoffStrategy</code> will become the
> > > * new <code>cumulativePeriod</code>. In this way, after each failure, you can \
> > >                 cause
> > > * the service to wait a longer and longer period of time before restarting.
> > > * Once the task completes successfully, the cumulativePeriod is reset to
> > > * the value of <code>period</code>.</p>
> > > *
> > > * <p>ScheduledService defines static EXPONENTIAL_BACKOFF_STRATEGY and \
> > >                 LOGARITHMIC_BACKOFF_STRATEGY
> > > * implementations, of which LOGARITHMIC_BACKOFF_STRATEGY is the default value \
> > >                 for
> > > * backoffStrategy. After <code>maximumFailureCount</code> is reached, the
> > > * ScheduledService will transition to FAILED in exactly the same way as if
> > > * <code>restartOnFailure</code> were false.</p>
> > > *
> > > * <p>If the <code>period</code> or <code>delay</code> is changed while the
> > > * ScheduledService is running, the new values will be taken into account on the
> > > * next iteration. For example, if the <code>period</code> is increased, then \
> > >                 the next time the
> > > * ScheduledService enters the SCHEDULED state, the new <code>period</code> will \
> > >                 be used.
> > > * Likewise, if the <code>delay</code> is changed, the new value will be honored \
> > >                 on
> > > * the next restart or reset/start.</p>
> > > *
> > > * The ScheduledService is typically used for use cases that involve polling. \
> > >                 For
> > > * example, you may want to ping a server on a regular basis to see if there are
> > > * any updates. Such as ScheduledService might be implemented like this:
> > > *
> > > * <pre><code>
> > > * ScheduledService&lt;Document&gt; svc = new \
> > >                 ScheduledService&lt;&gt;(Duration.seconds(1)) {
> > > *     protected Task&lt;Document&gt; createTask() {
> > > *         return new Task&lt;Document&gt;() {
> > > *             protected Document call() {
> > > *                 // Connect to a Server
> > > *                 // Get the XML document
> > > *                 // Parse it into a document
> > > *                 return document;
> > > *             }
> > > *         }
> > > *     }
> > > * }
> > > * </code></pre>
> > > *
> > > * This example will ping the remote server every 1 second.
> > > *
> > > * <p>Timing for this class is not absolutely reliable. A very busy event thread \
> > >                 might introduce some timing
> > > * lag into the beginning of the execution of the background Task, so very small \
> > >                 values for the period or
> > > * delay are likely to be inaccurate. A delay or period in the hundreds of \
> > >                 milliseconds or larger should be
> > > * fairly reliable.</p>
> > > *
> > > * <p>The ScheduledService in its default configuration has a default \
> > >                 <code>period</code> of 0 and a
> > > * default <code>delay</code> of 0. This will cause the ScheduledService to \
> > >                 execute the task immediately
> > > * upon {@link #start()}, and re-executing immediately upon successful \
> > >                 completion.</p>
> > > *
> > > * <p>For this purposes of this class, any Duration that answers true to {@link \
> > >                 javafx.util.Duration#isUnknown()}
> > > * will treat that duration as if it were Duration.ZERO. Likewise, any Duration \
> > >                 which answers true
> > > * to {@link javafx.util.Duration#isIndefinite()} will be treated as if it were \
> > >                 a duration of Double.MAX_VALUE
> > > * milliseconds. Any null Duration is treated as Duration.ZERO. Any custom \
> > >                 implementation of an backoff strategy
> > > * callback must be prepared to handle these different potential values.</p>
> > > *
> > > * <p>The ScheduledService introduces a new property called {@link #lastValue}. \
> > >                 The lastValue is the value that
> > > * was last successfully computed. Because a Service clears its {@code value} \
> > >                 property on each run, and
> > > * because the ScheduledService will reschedule a run immediately after \
> > >                 completion (unless it enters the
> > > * cancelled or failed states), the value property is not overly useful on a \
> > >                 ScheduledService. In most cases
> > > * you will want to instead use the value returned by lastValue.</p>
> > > *
> > > * <b>Implementer Note:</b> The {@link #ready()}, {@link #scheduled()}, {@link \
> > >                 #running()}, {@link #succeeded()},
> > > * {@link #cancelled()}, and {@link #failed()} methods are implemented in this \
> > >                 class. Subclasses which also
> > > * override these methods must take care to invoke the super implementation.
> > > *
> > > * @param <V> The computed value of the ScheduledService
> > > */
> > > public abstract class ScheduledService<V> extends Service<V> {
> > > /**
> > > * A Callback implementation for the <code>backoffStrategy</code> property which
> > > * will exponentially backoff the period between re-executions in the case of
> > > * a failure. This computation takes the original period and the number of
> > > * consecutive failures and computes the backoff amount from that information.
> > > *
> > > * <p>If the {@code service} is null, then Duration.ZERO is returned. If the \
> > >                 period is 0 then
> > > * the result of this method will simply be {@code \
> > >                 Math.exp(currentFailureCount)}. In all other cases,
> > > * the returned value is the same as {@code period + (period * \
> > >                 Math.exp(currentFailureCount))).</p>
> > > */
> > > public static final Callback<ScheduledService<?>, Duration> \
> > > EXPONENTIAL_BACKOFF_STRATEGY = new Callback<ScheduledService<?>, Duration>() {
> > > @Override public Duration call(ScheduledService<?> service) {...};
> > > 
> > > /**
> > > * A Callback implementation for the <code>backoffStrategy</code> property which
> > > * will logarithmically backoff the period between re-executions in the case of
> > > * a failure. This computation takes the original period and the number of
> > > * consecutive failures and computes the backoff amount from that information.
> > > *
> > > * <p>If the {@code service} is null, then Duration.ZERO is returned. If the \
> > >                 period is 0 then
> > > * the result of this method will simply be {@code \
> > >                 Math.log1p(currentFailureCount)}. In all other cases,
> > > * the returned value is the same as {@code period + (period * \
> > >                 Math.log1p(currentFailureCount))).</p>
> > > */
> > > public static final Callback<ScheduledService<?>, Duration> \
> > > LOGARITHMIC_BACKOFF_STRATEGY = new Callback<ScheduledService<?>, Duration>() \
> > > {...}; 
> > > /**
> > > * A Callback implementation for the <code>backoffStrategy</code> property which
> > > * will linearly backoff the period between re-executions in the case of
> > > * a failure. This computation takes the original period and the number of
> > > * consecutive failures and computes the backoff amount from that information.
> > > *
> > > * <p>If the {@code service} is null, then Duration.ZERO is returned. If the \
> > >                 period is 0 then
> > > * the result of this method will simply be {@code currentFailureCount}. In all \
> > >                 other cases,
> > > * the returned value is the same as {@code period + (period * \
> > >                 currentFailureCount).</p>
> > > */
> > > public static final Callback<ScheduledService<?>, Duration> \
> > > LINEAR_BACKOFF_STRATEGY = new Callback<ScheduledService<?>, Duration>() {...};
> > > 
> > > /**
> > > * This Timer is used to schedule the delays for each ScheduledService. A single \
> > >                 timer
> > > * ought to be able to easily service thousands of ScheduledService objects.
> > > */
> > > private static final Timer DELAY_TIMER = new Timer("ScheduledService Delay \
> > > Timer", true); 
> > > /**
> > > * The initial delay between when the ScheduledService is first started, and \
> > >                 when it will begin
> > > * operation. This is the amount of time the ScheduledService will remain in the \
> > >                 SCHEDULED state,
> > > * before entering the RUNNING state, following a fresh invocation of {@link \
> > >                 #start()} or {@link #restart()}.
> > > */
> > > private ObjectProperty<Duration> delay = new SimpleObjectProperty<>(this, \
> > > "delay", Duration.ZERO); public final Duration getDelay() { return delay.get(); \
> > > } public final void setDelay(Duration value) { delay.set(value); }
> > > public final ObjectProperty<Duration> delayProperty() { return delay; }
> > > 
> > > /**
> > > * The minimum amount of time to allow between the last time the service was in \
> > >                 the RUNNING state
> > > * until it should run again. The actual period (also known as \
> > >                 <code>cumulativePeriod</code>)
> > > * will depend on this property as well as the <code>backoffStrategy</code> and \
> > >                 number of failures.
> > > */
> > > private ObjectProperty<Duration> period = new SimpleObjectProperty<>(this, \
> > > "period", Duration.ZERO); public final Duration getPeriod() { return \
> > > period.get(); } public final void setPeriod(Duration value) { \
> > > period.set(value); } public final ObjectProperty<Duration> periodProperty() { \
> > > return period; } 
> > > /**
> > > * Computes the amount of time to add to the period on each failure. This \
> > >                 cumulative amount is reset whenever
> > > * the the ScheduledService is manually restarted.
> > > */
> > > private ObjectProperty<Callback<ScheduledService<?>,Duration>> backoffStrategy \
> > > = new SimpleObjectProperty<>(this, "backoffStrategy", \
> > > LOGARITHMIC_BACKOFF_STRATEGY); public final \
> > > Callback<ScheduledService<?>,Duration> getBackoffStrategy() { return \
> > > backoffStrategy.get(); } public final void \
> > > setBackoffStrategy(Callback<ScheduledService<?>, Duration> value) { \
> > > backoffStrategy.set(value); } public final \
> > > ObjectProperty<Callback<ScheduledService<?>,Duration>> \
> > > backoffStrategyProperty() { return backoffStrategy; } 
> > > /**
> > > * Indicates whether the ScheduledService should automatically restart in the \
> > >                 case of a failure in the Task.
> > > */
> > > private BooleanProperty restartOnFailure = new SimpleBooleanProperty(this, \
> > > "restartOnFailure", true); public final boolean getRestartOnFailure() { return \
> > > restartOnFailure.get(); } public final void setRestartOnFailure(boolean value) \
> > > { restartOnFailure.set(value); } public final BooleanProperty \
> > > restartOnFailureProperty() { return restartOnFailure; } 
> > > /**
> > > * The maximum number of times the ScheduledService can fail before it simply \
> > >                 ends in the FAILED
> > > * state. You can of course restart the ScheduledService manually, which will \
> > >                 cause the current
> > > * count to be reset.
> > > */
> > > private IntegerProperty maximumFailureCount = new SimpleIntegerProperty(this, \
> > > "maximumFailureCount", Integer.MAX_VALUE); public final int \
> > > getMaximumFailureCount() { return maximumFailureCount.get(); } public final \
> > > void setMaximumFailureCount(int value) { maximumFailureCount.set(value); } \
> > > public final IntegerProperty maximumFailureCountProperty() { return \
> > > maximumFailureCount; } 
> > > /**
> > > * The current number of times the ScheduledService has failed. This is reset \
> > >                 whenever the
> > > * ScheduledService is manually restarted.
> > > */
> > > private ReadOnlyIntegerWrapper currentFailureCount = new \
> > > ReadOnlyIntegerWrapper(this, "currentFailureCount", 0); public final int \
> > > getCurrentFailureCount() { return currentFailureCount.get(); } public final \
> > > ReadOnlyIntegerProperty currentFailureCountProperty() { return \
> > > currentFailureCount.getReadOnlyProperty(); } 
> > > /**
> > > * The current cumulative period in use between iterations. This will be the \
> > >                 same as <code>period</code>,
> > > * except after a failure, in which case the result of the backoffStrategy will \
> > >                 be used as the cumulative period
> > > * following each failure. This is reset whenever the ScheduledService is \
> > >                 manually restarted or an iteration
> > > * is successful. The cumulativePeriod is modified when the ScheduledService \
> > >                 enters the scheduled state.
> > > * The cumulativePeriod can be capped by setting the {@code \
> > >                 maximumCumulativePeriod}.
> > > */
> > > private ReadOnlyObjectWrapper<Duration> cumulativePeriod = new \
> > > ReadOnlyObjectWrapper<>(this, "cumulativePeriod", Duration.ZERO); public final \
> > > Duration getCumulativePeriod() { return cumulativePeriod.get(); } public final \
> > > ReadOnlyObjectProperty<Duration> cumulativePeriodProperty() { return \
> > > cumulativePeriod.getReadOnlyProperty(); } 
> > > /**
> > > * The maximum allowed value for the cumulativePeriod. Setting this value will \
> > >                 help ensure that in the case of
> > > * repeated failures the back-off algorithm doesn't end up producing \
> > >                 unreasonably large values for
> > > * cumulative period. The cumulative period is guaranteed not to be any larger \
> > >                 than this value. If the
> > > * maximumCumulativePeriod is negative, then cumulativePeriod will be capped at \
> > >                 0. If maximumCumulativePeriod
> > > * is NaN or null, then it will not influence the cumulativePeriod.
> > > */
> > > private ObjectProperty<Duration> maximumCumulativePeriod = new \
> > > SimpleObjectProperty<>(this, "maximumCumulativePeriod", Duration.INDEFINITE); \
> > > public final Duration getMaximumCumulativePeriod() { return \
> > > maximumCumulativePeriod.get(); } public final void \
> > > setMaximumCumulativePeriod(Duration value) { \
> > > maximumCumulativePeriod.set(value); } public final ObjectProperty<Duration> \
> > > maximumCumulativePeriodProperty() { return maximumCumulativePeriod; } 
> > > /**
> > > * The last successfully computed value. During each iteration, the "value" of \
> > >                 the ScheduledService will be
> > > * reset to null, as with any other Service. The "lastValue" however will be set \
> > >                 to the most recently
> > > * successfully computed value, even across iterations. It is reset however \
> > >                 whenever you manually call
> > > * reset or restart.
> > > */
> > > private ReadOnlyObjectWrapper<V> lastValue = new ReadOnlyObjectWrapper<>(this, \
> > > "lastValue", null); public final V getLastValue() { return lastValue.get(); }
> > > public final ReadOnlyObjectProperty lastValueProperty() { return \
> > > lastValue.getReadOnlyProperty(); } 
> > > 
> > > On Dec 23, 2011, at 4:10 PM, Richard Bair <richard.bair@oracle.com> wrote:
> > > 
> > > > Hi,
> > > > 
> > > > http://javafx-jira.kenai.com/browse/RT-18702
> > > > 
> > > > While waiting for builds and such, I implemented a ScheduledService. Several \
> > > > folks on the forums had asked for something like this, so I thought I'd take \
> > > > a shot at writing a complete implementation that could make it into 2.1. I \
> > > > would end up writing all the unit tests for this, and total development time \
> > > > is estimated at no more than 4 days (so far I've got 4 hours put into it and \
> > > > it isn't in too shabby of condition, so I think the estimate is reasonable). 
> > > > Attached to the issue is the implementation. I'd love if somebody would like \
> > > > to take a look, particularly at the API. The name comes from the \
> > > > java.util.concurrent package, which has a ScheduledExecutorService. Since \
> > > > this is performing a similar function, a similar name seemed in order. In \
> > > > addition, the "period" name comes from that package as well (since the names \
> > > > used in the animation package I think have different semantics, so using a \
> > > > different name is a good idea here, I think). 
> > > > Here it is:
> > > > 
> > > > /**
> > > > * <p>
> > > > *     The ScheduledService is a service which will automatically restart
> > > > *     itself after a successful execution, and under some conditions will
> > > > *     restart even in case of failure. A new ScheduledService begins in
> > > > *     the READY state, just as a normal Service. After calling
> > > > *     <code>start</code> or <code>restart</code>, the ScheduledService will
> > > > *     enter the SCHEDULED state for the duration specified by \
> > > >                 <code>delay</code>.
> > > > * </p>
> > > > * <p>
> > > > *     Once RUNNING, the ScheduledService will execute its Task. On successful
> > > > *     completion, the ScheduledService will transition to the SUCCEEDED \
> > > >                 state,
> > > > *     and then to the READY state and back to the SCHEDULED state. The amount
> > > > *     of time the ScheduledService will remain in this state depends on the
> > > > *     amount of time between the last state transition to RUNNING, and the
> > > > *     current time, and the <code>period</code>. In short, the \
> > > >                 <code>period</code>
> > > > *     defines the minimum amount of time between executions. If the previous
> > > > *     execution completed before <code>period</code> expires, then the
> > > > *     ScheduledService will remain in the SCHEDULED state until the period
> > > > *     expires. If on the other hand the execution took longer than the
> > > > *     specified period, then the ScheduledService will immediately transition
> > > > *     back to RUNNING.
> > > > * </p>
> > > > * <p>
> > > > *     If, while RUNNING, the ScheduledService's Task throws an error or in
> > > > *     some other way ends up transitioning to FAILED, then the \
> > > >                 ScheduledService
> > > > *     will either restart or quit, depending on the values for
> > > > *     <code>computeEaseOff</code>, <code>restartOnFailure</code>, and
> > > > *     <code>maximumFailureCount</code>.
> > > > * </p>
> > > > * <p>
> > > > *     If a failure occurs and <code>restartOnFailure</code> is false, then
> > > > *     the ScheduledService will transition to FAILED and will stop. To \
> > > >                 restart
> > > > *     a failed ScheduledService, you must call restart manually.
> > > > * </p>
> > > > * <p>
> > > > *     If a failure occurs and <code>restartOnFailure</code> is true, then
> > > > *     the the ScheduledService <em>may</em> restart automatically. First,
> > > > *     the result of calling <code>computeEaseOff</code> will become the
> > > > *     new <code>cumulativePeriod</code>. In this way, after each failure, you \
> > > >                 can cause
> > > > *     the service to wait a longer and longer period of time before \
> > > >                 restarting.
> > > > *     ScheduledService defines static EXPONENTIAL_EASE_OFF and \
> > > >                 LOGARITHMIC_EASE_OFF
> > > > *     implementations, of which LOGARITHMIC_EASE_OFF is the default value of
> > > > *     computeEaseOff. After <code>maximumFailureCount</code> is reached, the
> > > > *     ScheduledService will transition to FAILED in exactly the same way as \
> > > >                 if
> > > > *     <code>restartOnFailure</code> were false.
> > > > * </p>
> > > > */
> > > > 
> > > > /**
> > > > * A Callback implementation for the <code>computeEaseOff</code> property \
> > > >                 which
> > > > * will exponentially ease off the period between re-executions in the case of
> > > > * a failure. This computation takes the original period and the number of
> > > > * consecutive failures and computes the ease off amount from that \
> > > >                 information.
> > > > */
> > > > public static final Callback<ScheduledService<?>, Duration> \
> > > > EXPONENTIAL_EASE_OFF.... 
> > > > /**
> > > > * A Callback implementation for the <code>computeEaseOff</code> property \
> > > >                 which
> > > > * will logarithmically ease off the period between re-executions in the case \
> > > >                 of
> > > > * a failure. This computation takes the original period and the number of
> > > > * consecutive failures and computes the ease off amount from that \
> > > >                 information.
> > > > */
> > > > public static final Callback<ScheduledService<?>, Duration> \
> > > > LOGARITHMIC_EASE_OFF.... 
> > > > /**
> > > > * The initial delay between when the ScheduledService is first started, and \
> > > >                 when it will begin
> > > > * operation. This is the amount of time the ScheduledService will remain in \
> > > >                 the SCHEDULED state,
> > > > * before entering the RUNNING state.
> > > > */
> > > > private ObjectProperty<Duration> delay = new \
> > > >                 SimpleObjectProperty<Duration>(this, "delay", Duration.ZERO);
> > > > ....
> > > > 
> > > > /**
> > > > * The minimum amount of time to allow between the last time the service was \
> > > >                 in the RUNNING state
> > > > * until it should run again. The actual period (also known as \
> > > >                 <code>cumulativePeriod</code>)
> > > > * will depend on this property as well as the <code>computeEaseOff</code> and \
> > > >                 number of failures.
> > > > */
> > > > private ObjectProperty<Duration> period = new \
> > > >                 SimpleObjectProperty<Duration>(this, "period", \
> > > >                 Duration.ZERO);
> > > > ....
> > > > 
> > > > /**
> > > > * Computes the amount of time to add to the period on each failure. This \
> > > >                 cumulative amount is reset whenever
> > > > * the the ScheduledService is manually restarted. The Callback takes a \
> > > >                 Duration, which is the last
> > > > * <code>cumulativePeriod</code>, and returns a Duration which will be the new \
> > > >                 <code>cumulativePeriod</code>.
> > > > */
> > > > private ObjectProperty<Callback<ScheduledService<?>,Duration>> computeEaseOff \
> > > > = new SimpleObjectProperty<Callback<ScheduledService<?>,Duration>>(this, \
> > > >                 "computeEaseOff", LOGARITHMIC_EASE_OFF);
> > > > ....
> > > > 
> > > > /**
> > > > * Indicates whether the ScheduledService should automatically restart in the \
> > > >                 case of a failure.
> > > > */
> > > > private BooleanProperty restartOnFailure = new SimpleBooleanProperty(this, \
> > > >                 "restartOnFailure", false);
> > > > ....
> > > > 
> > > > /**
> > > > * The maximum number of times the ScheduledService can fail before it simply \
> > > >                 ends in the FAILED
> > > > * state. You can of course restart the ScheduledService manually, which will \
> > > >                 cause the current
> > > > * count to be reset.
> > > > */
> > > > private IntegerProperty maximumFailureCount = new SimpleIntegerProperty(this, \
> > > >                 "maximumFailureCount", Integer.MAX_VALUE);
> > > > ....
> > > > 
> > > > /**
> > > > * The current number of times the ScheduledService has failed. This is reset \
> > > >                 whenever the
> > > > * ScheduledService is manually restarted.
> > > > */
> > > > private ReadOnlyIntegerWrapper currentFailureCount = new \
> > > >                 ReadOnlyIntegerWrapper(this, "currentFailureCount", 0);
> > > > ....
> > > > 
> > > > /**
> > > > * The current cumulative period in use between iterations. This will be the \
> > > >                 same as <code>period</code>,
> > > > * except after a failure, in which case the <code>computeEaseOff</code> will \
> > > >                 compute a new period. This
> > > > * is reset whenever the ScheduledService is manually restarted.
> > > > */
> > > > private ReadOnlyObjectWrapper<Duration> cumulativePeriod = new \
> > > >                 ReadOnlyObjectWrapper<Duration>(this, "cumulativePeriod", \
> > > >                 Duration.ZERO);
> > > > ....
> > > > 
> > > > /**
> > > > * The last successfully computed value. During each iteration, the "value" of \
> > > >                 the ScheduledService will be
> > > > * reset to null, as with any other Service. The "lastValue" however will be \
> > > >                 set to the most currently
> > > > * successfully computed value, even across iterations. It is reset however \
> > > >                 whenever you manually call
> > > > * reset or restart.
> > > > */
> > > > private ReadOnlyObjectWrapper<V> lastValue = new \
> > > >                 ReadOnlyObjectWrapper<V>(this, "lastValue", null);
> > > > ....
> > > > 
> > > > 
> > > > It seems pretty powerful and flexible but the API isn't too bad. I played \
> > > > with a couple configurations but this one seemed to play best with the \
> > > > existing semantics of the Service. For example, on Service the "value" is set \
> > > > to null whenever the Service is reset for another run. But this doesn't work \
> > > > as well for an auto-restarting service like ScheduledService. So rather than \
> > > > break the semantic that "value" is set to null on restart, I added a new \
> > > > variable "lastValue" which remembers the last value even across failures, and \
> > > > is only rest when the developer manually resets or restarts things. During an \
> > > > auto-restart iteration, the lastValue is not changed (it is only updated to \
> > > > the last "value" of a successful run). 
> > > > Thanks
> > > > Richard
> > > 
> > 
> 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic