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

List:       openjdk-openjfx-dev
Subject:    [REVIEW] Auto-restartable ScheduledService implementation
From:       richard.bair () oracle ! com (Richard Bair)
Date:       2011-12-24 0:26:00
Message-ID: 93A6A9F7-90A0-442C-B5DF-C99EF8C2B9BF () oracle ! com
[Download RAW message or body]

Wow, and very satisfying to see that the required change to Service to support this \
class is now in the openjfx rt repo. So go update, rebuild, and use the file attached \
to this issue to give it a spin.

Richard

On Dec 23, 2011, at 4:10 PM, Richard Bair 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