[LANG-501] Added BackgroundInitializer class with JUnit tests.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@831586 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
86008e6c48
commit
8c413aecd8
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.lang.concurrent;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A class that allows complex initialization operations in a background task.
|
||||
* </p>
|
||||
* <p>
|
||||
* Applications often have to do some expensive initialization steps when they
|
||||
* are started, e.g. constructing a connection to a database, reading a
|
||||
* configuration file, etc. Doing these things in parallel can enhance
|
||||
* performance as the CPU load can be improved. However, when access to the
|
||||
* resources initialized in a background thread is actually required,
|
||||
* synchronization has to be performed to ensure that their initialization is
|
||||
* complete.
|
||||
* </p>
|
||||
* <p>
|
||||
* This abstract base class provides support for this use case. A concrete
|
||||
* subclass must implement the {@link #initialize()} method. Here an arbitrary
|
||||
* initialization can be implemented, and a result object can be returned. With
|
||||
* this method in place the basic usage of this class is as follows (where
|
||||
* {@code MyBackgroundInitializer} is a concrete subclass):
|
||||
*
|
||||
* <pre>
|
||||
* MyBackgroundInitializer initializer = new MyBackgroundInitializer();
|
||||
* initializer.start();
|
||||
* // Now do some other things. Initialization runs in a parallel thread
|
||||
* ...
|
||||
* // Wait for the end of initialization and access the result object
|
||||
* Object result = initializer.get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* After the construction of a {@code BackgroundInitializer} object its
|
||||
* {@link #start()} method has to be called. This starts the background
|
||||
* processing. The application can now continue to do other things. When it
|
||||
* needs access to the object produced by the {@code BackgroundInitializer} it
|
||||
* calls its {@link #get()} method. If initialization is already complete,
|
||||
* {@link #get()} returns the result object immediately. Otherwise it blocks
|
||||
* until the result object is fully constructed.
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code BackgroundInitializer} is a thin wrapper around a {@code Future}
|
||||
* object and uses an {@code ExecutorService} for running the background
|
||||
* initialization task. It is possible to pass in an {@code ExecutorService} at
|
||||
* construction time or set one using {@code setExternalExecutor()} before
|
||||
* {@code start()} was called. Then this object is used to spawn the background
|
||||
* task. If no {@code ExecutorService} has been provided, {@code
|
||||
* BackgroundInitializer} creates a temporary {@code ExecutorService} and
|
||||
* destroys it when initialization is complete.
|
||||
* </p>
|
||||
* <p>
|
||||
* The methods provided by {@code BackgroundInitializer} provide for minimal
|
||||
* interaction with the wrapped {@code Future} object. It is also possible to
|
||||
* obtain the {@code Future} object directly. Then the enhanced functionality
|
||||
* offered by {@code Future} can be used, e.g. to check whether the background
|
||||
* operation is complete or to cancel the operation.
|
||||
* </p>
|
||||
*
|
||||
* @version $Id$
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public abstract class BackgroundInitializer<T> {
|
||||
/** The external executor service for executing tasks. */
|
||||
private ExecutorService externalExecutor;
|
||||
|
||||
/** A reference to the executor service that is actually used. */
|
||||
private ExecutorService executor;
|
||||
|
||||
/** Stores the handle to the background task. */
|
||||
private Future<T> future;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code BackgroundInitializer}. No external
|
||||
* {@code ExecutorService} is used.
|
||||
*/
|
||||
protected BackgroundInitializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code BackgroundInitializer} and initializes
|
||||
* it with the given {@code ExecutorService}. If the {@code ExecutorService}
|
||||
* is not null, the background task for initializing this object will be
|
||||
* scheduled at this service. Otherwise a new temporary {@code
|
||||
* ExecutorService} is created.
|
||||
*
|
||||
* @param exec an external {@code ExecutorService} to be used for task
|
||||
* execution
|
||||
*/
|
||||
protected BackgroundInitializer(ExecutorService exec) {
|
||||
setExternalExecutor(exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the external {@code ExecutorService} to be used by this class.
|
||||
*
|
||||
* @return the {@code ExecutorService}
|
||||
*/
|
||||
public final synchronized ExecutorService getExternalExecutor() {
|
||||
return externalExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag whether this {@code BackgroundInitializer} has already
|
||||
* been started.
|
||||
*
|
||||
* @return a flag whether the {@link #start()} method has already been
|
||||
* called
|
||||
*/
|
||||
public synchronized boolean isStarted() {
|
||||
return future != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@code ExecutorService} to be used by this class. The {@code
|
||||
* ExecutorService} passed to this method is used for executing the
|
||||
* background task. Thus it is possible to re-use an already existing
|
||||
* {@code ExecutorService} or to use a specially configured one. If no
|
||||
* {@code ExecutorService} is set, this instance creates a temporary one and
|
||||
* destroys it after background initialization is complete. Note that this
|
||||
* method must be called before {@link #start()}; otherwise an exception is
|
||||
* thrown.
|
||||
*
|
||||
* @param externalExecutor the {@code ExecutorService} to be used
|
||||
* @throws IllegalStateException if this initializer has already been
|
||||
* started
|
||||
*/
|
||||
public final synchronized void setExternalExecutor(
|
||||
ExecutorService externalExecutor) {
|
||||
if (isStarted()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot set ExecutorService after start()!");
|
||||
}
|
||||
|
||||
this.externalExecutor = externalExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the background initialization. With this method the initializer
|
||||
* becomes active and invokes the {@link #initialize()} method in a
|
||||
* background task. A {@code BackgroundInitializer} can be started exactly
|
||||
* once. The return value of this method determines whether the start was
|
||||
* successful: only the first invocation of this method returns <b>true</b>,
|
||||
* following invocations will return <b>false</b>.
|
||||
*
|
||||
* @return a flag whether the initializer could be started successfully
|
||||
*/
|
||||
public synchronized boolean start() {
|
||||
// Not yet started?
|
||||
if (!isStarted()) {
|
||||
|
||||
// Determine the executor to use and whether a temporary one has to
|
||||
// be created
|
||||
ExecutorService tempExec;
|
||||
executor = getExternalExecutor();
|
||||
if (executor == null) {
|
||||
executor = tempExec = createExecutor();
|
||||
} else {
|
||||
tempExec = null;
|
||||
}
|
||||
|
||||
future = executor.submit(createTask(tempExec));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the background initialization. This method blocks
|
||||
* until initialization is complete. If the background processing caused a
|
||||
* runtime exception, it is directly thrown by this method. Checked
|
||||
* exceptions, including {@code InterruptedException} are wrapped in a
|
||||
* {@link ConcurrentException}. Calling this method before {@link #start()}
|
||||
* was called causes an {@code IllegalStateException} exception to be
|
||||
* thrown.
|
||||
*
|
||||
* @return the object produced by this initializer
|
||||
* @throws ConcurrentException if a checked exception occurred during
|
||||
* background processing
|
||||
* @throws IllegalStateException if {@link #start()} has not been called
|
||||
*/
|
||||
public T get() throws ConcurrentException {
|
||||
try {
|
||||
return getFuture().get();
|
||||
} catch (ExecutionException execex) {
|
||||
ConcurrentUtils.handleCause(execex);
|
||||
return null; // should not be reached
|
||||
} catch (InterruptedException iex) {
|
||||
// reset interrupted state
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ConcurrentException(iex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Future} object that was created when {@link #start()}
|
||||
* was called. Therefore this method can only be called after {@code
|
||||
* start()}.
|
||||
*
|
||||
* @return the {@code Future} object wrapped by this initializer
|
||||
* @throws IllegalStateException if {@link #start()} has not been called
|
||||
*/
|
||||
public synchronized Future<T> getFuture() {
|
||||
if (future == null) {
|
||||
throw new IllegalStateException("start() must be called first!");
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code ExecutorService} that is actually used for executing
|
||||
* the background task. This method can be called after {@link #start()}
|
||||
* (before {@code start()} it returns <b>null</b>). If an external executor
|
||||
* was set, this is also the active executor. Otherwise this method returns
|
||||
* the temporary executor that was created by this object.
|
||||
*
|
||||
* @return the {@code ExecutorService} for executing the background task
|
||||
*/
|
||||
protected synchronized final ExecutorService getActiveExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of background tasks to be created for this
|
||||
* initializer. This information is evaluated when a temporary {@code
|
||||
* ExecutorService} is created. This base implementation returns 1. Derived
|
||||
* classes that do more complex background processing can override it. This
|
||||
* method is called from a synchronized block by the {@link #start()}
|
||||
* method. Therefore overriding methods should be careful with obtaining
|
||||
* other locks and return as fast as possible.
|
||||
*
|
||||
* @return the number of background tasks required by this initializer
|
||||
*/
|
||||
protected int getTaskCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the initialization. This method is called in a background task
|
||||
* when this {@code BackgroundInitializer} is started. It must be
|
||||
* implemented by a concrete subclass. An implementation is free to perform
|
||||
* arbitrary initialization. The object returned by this method can be
|
||||
* queried using the {@link #get()} method.
|
||||
*
|
||||
* @return a result object
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
protected abstract T initialize() throws Exception;
|
||||
|
||||
/**
|
||||
* Creates a task for the background initialization. The {@code Callable}
|
||||
* object returned by this method is passed to the {@code ExecutorService}.
|
||||
* This implementation returns a task that invokes the {@link #initialize()}
|
||||
* method. If a temporary {@code ExecutorService} is used, it is destroyed
|
||||
* at the end of the task.
|
||||
*
|
||||
* @param execDestory the {@code ExecutorService} to be destroyed by the
|
||||
* task
|
||||
* @return a task for the background initialization
|
||||
*/
|
||||
private Callable<T> createTask(ExecutorService execDestroy) {
|
||||
return new InitializationTask(execDestroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@code ExecutorService} to be used. This method is called if
|
||||
* no {@code ExecutorService} was provided at construction time.
|
||||
*
|
||||
* @return the {@code ExecutorService} to be used
|
||||
*/
|
||||
private ExecutorService createExecutor() {
|
||||
return Executors.newFixedThreadPool(getTaskCount());
|
||||
}
|
||||
|
||||
private class InitializationTask implements Callable<T> {
|
||||
/** Stores the executor service to be destroyed at the end. */
|
||||
private final ExecutorService executor;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code InitializationTask} and initializes
|
||||
* it with the {@code ExecutorService} to be destroyed at the end.
|
||||
*
|
||||
* @param exec the {@code ExecutorService}
|
||||
*/
|
||||
public InitializationTask(ExecutorService exec) {
|
||||
executor = exec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates initialization and returns the result.
|
||||
*
|
||||
* @return the result object
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
public T call() throws Exception {
|
||||
try {
|
||||
return initialize();
|
||||
} finally {
|
||||
if (executor != null) {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.lang.concurrent;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class BackgroundInitializerTest extends TestCase {
|
||||
/**
|
||||
* Helper method for checking whether the initialize() method was correctly
|
||||
* called. start() must already have been invoked.
|
||||
*
|
||||
* @param init the initializer to test
|
||||
*/
|
||||
private void checkInitialize(BackgroundInitializerTestImpl init) {
|
||||
try {
|
||||
Integer result = init.get();
|
||||
assertEquals("Wrong result", 1, result.intValue());
|
||||
assertEquals("Wrong number of invocations", 1, init.initializeCalls);
|
||||
assertNotNull("No future", init.getFuture());
|
||||
} catch (ConcurrentException cex) {
|
||||
fail("Unexpected exception: " + cex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether initialize() is invoked.
|
||||
*/
|
||||
public void testInitialize() throws ConcurrentException {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
init.start();
|
||||
checkInitialize(init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to obtain the executor before start(). It should not have been
|
||||
* initialized yet.
|
||||
*/
|
||||
public void testGetActiveExecutorBeforeStart() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
assertNull("Got an executor", init.getActiveExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an external executor is correctly detected.
|
||||
*/
|
||||
public void testGetActiveExecutorExternal() {
|
||||
ExecutorService exec = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(
|
||||
exec);
|
||||
init.start();
|
||||
assertSame("Wrong executor", exec, init.getActiveExecutor());
|
||||
checkInitialize(init);
|
||||
} finally {
|
||||
exec.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getActiveExecutor() for a temporary executor.
|
||||
*/
|
||||
public void testGetActiveExecutorTemp() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
init.start();
|
||||
assertNotNull("No active executor", init.getActiveExecutor());
|
||||
checkInitialize(init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the execution of the background task if a temporary executor has to
|
||||
* be created.
|
||||
*/
|
||||
public void testInitializeTempExecutor() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
assertTrue("Wrong result of start()", init.start());
|
||||
checkInitialize(init);
|
||||
assertTrue("Executor not shutdown", init.getActiveExecutor()
|
||||
.isShutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an external executor can be set using the
|
||||
* setExternalExecutor() method.
|
||||
*/
|
||||
public void testSetExternalExecutor() throws Exception {
|
||||
ExecutorService exec = Executors.newCachedThreadPool();
|
||||
try {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
init.setExternalExecutor(exec);
|
||||
assertEquals("Wrong executor service", exec, init
|
||||
.getExternalExecutor());
|
||||
assertTrue("Wrong result of start()", init.start());
|
||||
assertSame("Wrong active executor", exec, init.getActiveExecutor());
|
||||
checkInitialize(init);
|
||||
assertFalse("Executor was shutdown", exec.isShutdown());
|
||||
} finally {
|
||||
exec.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that setting an executor after start() causes an exception.
|
||||
*/
|
||||
public void testSetExternalExecutorAfterStart() throws ConcurrentException {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
init.start();
|
||||
try {
|
||||
init.setExternalExecutor(Executors.newSingleThreadExecutor());
|
||||
fail("Could set executor after start()!");
|
||||
} catch (IllegalStateException istex) {
|
||||
init.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests invoking start() multiple times. Only the first invocation should
|
||||
* have an effect.
|
||||
*/
|
||||
public void testStartMultipleTimes() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
assertTrue("Wrong result for start()", init.start());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertFalse("Could start again", init.start());
|
||||
}
|
||||
checkInitialize(init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calling get() before start(). This should cause an exception.
|
||||
*/
|
||||
public void testGetBeforeStart() throws ConcurrentException {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
try {
|
||||
init.get();
|
||||
fail("Could call get() before start()!");
|
||||
} catch (IllegalStateException istex) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get() method if background processing causes a runtime
|
||||
* exception.
|
||||
*/
|
||||
public void testGetRuntimeException() throws Exception {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
RuntimeException rex = new RuntimeException();
|
||||
init.ex = rex;
|
||||
init.start();
|
||||
try {
|
||||
init.get();
|
||||
fail("Exception not thrown!");
|
||||
} catch (Exception ex) {
|
||||
assertEquals("Runtime exception not thrown", rex, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get() method if background processing causes a checked
|
||||
* exception.
|
||||
*/
|
||||
public void testGetCheckedException() throws Exception {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
Exception ex = new Exception();
|
||||
init.ex = ex;
|
||||
init.start();
|
||||
try {
|
||||
init.get();
|
||||
fail("Exception not thrown!");
|
||||
} catch (ConcurrentException cex) {
|
||||
assertEquals("Exception not thrown", ex, cex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get() method if waiting for the initialization is interrupted.
|
||||
*/
|
||||
public void testGetInterruptedException() throws Exception {
|
||||
ExecutorService exec = Executors.newSingleThreadExecutor();
|
||||
final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(
|
||||
exec);
|
||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
||||
init.shouldSleep = true;
|
||||
init.start();
|
||||
final AtomicReference<InterruptedException> iex = new AtomicReference<InterruptedException>();
|
||||
Thread getThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
init.get();
|
||||
} catch (ConcurrentException cex) {
|
||||
if (cex.getCause() instanceof InterruptedException) {
|
||||
iex.set((InterruptedException) cex.getCause());
|
||||
}
|
||||
} finally {
|
||||
assertTrue("Thread not interrupted", isInterrupted());
|
||||
latch1.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
getThread.start();
|
||||
getThread.interrupt();
|
||||
latch1.await();
|
||||
exec.shutdownNow();
|
||||
exec.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||
assertNotNull("No interrupted exception", iex.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests isStarted() before start() was called.
|
||||
*/
|
||||
public void testIsStartedFalse() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
assertFalse("Already started", init.isStarted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests isStarted() after start().
|
||||
*/
|
||||
public void testIsStartedTrue() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
init.start();
|
||||
assertTrue("Not started", init.isStarted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests isStarted() after the background task has finished.
|
||||
*/
|
||||
public void testIsStartedAfterGet() {
|
||||
BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
|
||||
init.start();
|
||||
checkInitialize(init);
|
||||
assertTrue("Not started", init.isStarted());
|
||||
}
|
||||
|
||||
/**
|
||||
* A concrete implementation of BackgroundInitializer. It also overloads
|
||||
* some methods that simplify testing.
|
||||
*/
|
||||
private static class BackgroundInitializerTestImpl extends
|
||||
BackgroundInitializer<Integer> {
|
||||
/** An exception to be thrown by initialize(). */
|
||||
Exception ex;
|
||||
|
||||
/** A flag whether the background task should sleep a while. */
|
||||
boolean shouldSleep;
|
||||
|
||||
/** The number of invocations of initialize(). */
|
||||
volatile int initializeCalls;
|
||||
|
||||
public BackgroundInitializerTestImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BackgroundInitializerTestImpl(ExecutorService exec) {
|
||||
super(exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records this invocation. Optionally throws an exception or sleeps a
|
||||
* while.
|
||||
*/
|
||||
@Override
|
||||
protected Integer initialize() throws Exception {
|
||||
if (ex != null) {
|
||||
throw ex;
|
||||
}
|
||||
if (shouldSleep) {
|
||||
Thread.sleep(60000L);
|
||||
}
|
||||
return ++initializeCalls;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue