[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