diff --git a/src/java/org/apache/commons/lang/concurrent/CallableBackgroundInitializer.java b/src/java/org/apache/commons/lang/concurrent/CallableBackgroundInitializer.java new file mode 100644 index 000000000..d84034030 --- /dev/null +++ b/src/java/org/apache/commons/lang/concurrent/CallableBackgroundInitializer.java @@ -0,0 +1,127 @@ +/* + * 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.ExecutorService; + +/** + *

+ * A specialized {@link BackgroundInitializer} implementation that wraps a + * {@code Callable} object. + *

+ *

+ * An instance of this class is initialized with a {@code Callable} object when + * it is constructed. The implementation of the {@link #initialize()} method + * defined in the super class delegates to this {@code Callable} so that the + * {@code Callable} is executed in the background thread. + *

+ *

+ * The {@code java.util.concurrent.Callable} interface is a standard mechanism + * of the JDK to define tasks to be executed by another thread. The {@code + * CallableBackgroundInitializer} class allows combining this standard interface + * with the background initializer API. + *

+ *

+ * Usage of this class is very similar to the default usage pattern of the + * {@link BackgroundInitializer} class: Just create an instance and provide the + * {@code Callable} object to be executed, then call the initializer's + * {@link #start()} method. This causes the {@code Callable} to be executed in + * another thread. When the results of the {@code Callable} are needed the + * initializer's {@link #get()} method can be called (which may block until + * background execution is complete). The following code fragment shows a + * typical usage example: + * + *

+ * // a Callable that performs a complex computation
+ * Callable computationCallable = new MyComputationCallable();
+ *
+ * // setup the background initializer
+ * CallableBackgroundInitializer initializer =
+ *     new CallableBackgroundInitializer(computationCallable);
+ * initializer.start();
+ *
+ * // Now do some other things. Initialization runs in a parallel thread
+ * ...
+ * // Wait for the end of initialization and access the result
+ * Integer result = initializer.get();
+ * 
+ * + *

+ * + * @version $Id: $ + * @param the type of the object managed by this initializer class + */ +public class CallableBackgroundInitializer extends BackgroundInitializer { + /** The Callable to be executed. */ + private final Callable callable; + + /** + * Creates a new instance of {@code CallableBackgroundInitializer} and sets + * the {@code Callable} to be executed in a background thread. + * + * @param call the {@code Callable} (must not be null) + * @throws IllegalArgumentException if the {@code Callable} is null + */ + public CallableBackgroundInitializer(Callable call) { + checkCallable(call); + callable = call; + } + + /** + * Creates a new instance of {@code CallableBackgroundInitializer} and + * initializes it with the {@code Callable} to be executed in a background + * thread and the {@code ExecutorService} for managing the background + * execution. + * + * @param call the {@code Callable} (must not be null) + * @param exec an external {@code ExecutorService} to be used for task + * execution + * @throws IllegalArgumentException if the {@code Callable} is null + */ + public CallableBackgroundInitializer(Callable call, ExecutorService exec) { + super(exec); + checkCallable(call); + callable = call; + } + + /** + * Performs initialization in a background thread. This implementation + * delegates to the {@code Callable} passed at construction time of this + * object. + * + * @return the result of the initialization + * @throws Exception if an error occurs + */ + @Override + protected T initialize() throws Exception { + return callable.call(); + } + + /** + * Tests the passed in {@code Callable} and throws an exception if it is + * undefined. + * + * @param call the object to check + * @throws IllegalArgumentException if the {@code Callable} is null + */ + private void checkCallable(Callable call) { + if (call == null) { + throw new IllegalArgumentException("Callable must not be null!"); + } + } +} diff --git a/src/test/org/apache/commons/lang/concurrent/CallableBackgroundInitializerTest.java b/src/test/org/apache/commons/lang/concurrent/CallableBackgroundInitializerTest.java new file mode 100644 index 000000000..a394fd1ce --- /dev/null +++ b/src/test/org/apache/commons/lang/concurrent/CallableBackgroundInitializerTest.java @@ -0,0 +1,99 @@ +/* + * 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.ExecutorService; +import java.util.concurrent.Executors; + +import junit.framework.TestCase; + +/** + * Test class for {@code CallableBackgroundInitializer} + * + * @version $Id$ + */ +public class CallableBackgroundInitializerTest extends TestCase { + /** Constant for the result of the call() invocation. */ + private static final Integer RESULT = 42; + + /** + * Tries to create an instance without a Callable. This should cause an + * exception. + */ + public void testInitNullCallable() { + try { + new CallableBackgroundInitializer(null); + fail("Could create instance without a Callable!"); + } catch (IllegalArgumentException iex) { + // ok + } + } + + /** + * Tests whether the executor service is correctly passed to the super + * class. + */ + public void testInitExecutor() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + CallableBackgroundInitializer init = new CallableBackgroundInitializer( + new TestCallable(), exec); + assertEquals("Executor not set", exec, init.getExternalExecutor()); + } + + /** + * Tries to pass a null Callable to the constructor that takes an executor. + * This should cause an exception. + */ + public void testInitExecutorNullCallable() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + new CallableBackgroundInitializer(null, exec); + fail("Could create instance without a Callable!"); + } catch (IllegalArgumentException iex) { + // ok + } + } + + /** + * Tests the implementation of initialize(). + */ + public void testInitialize() throws Exception { + TestCallable call = new TestCallable(); + CallableBackgroundInitializer init = new CallableBackgroundInitializer( + call); + assertEquals("Wrong result", RESULT, init.initialize()); + assertEquals("Wrong number of invocations", 1, call.callCount); + } + + /** + * A test Callable implementation for checking the initializer's + * implementation of the initialize() method. + */ + private static class TestCallable implements Callable { + /** A counter for the number of call() invocations. */ + int callCount; + + /** + * Records this invocation and returns the test result. + */ + public Integer call() throws Exception { + callCount++; + return RESULT; + } + } +}