commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r831587 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
Date Sat, 31 Oct 2009 19:52:58 GMT
Author: oheger
Date: Sat Oct 31 19:52:58 2009
New Revision: 831587

URL: http://svn.apache.org/viewvc?rev=831587&view=rev
Log:
[LANG-501] Added MultiBackgroundInitializer class with JUnit tests.

Added:
    commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
  (with props)
    commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
  (with props)

Added: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java?rev=831587&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
(added)
+++ commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
Sat Oct 31 19:52:58 2009
@@ -0,0 +1,340 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * <p>
+ * A specialized {@link BackgroundInitializer} implementation that can deal with
+ * multiple background initialization tasks.
+ * </p>
+ * <p>
+ * This class has a similar purpose as {@link BackgroundInitializer}. However,
+ * it is not limited to a single background initialization task. Rather it
+ * manages an arbitrary number of {@code BackgroundInitializer} objects,
+ * executes them, and waits until they are completely initialized. This is
+ * useful for applications that have to perform multiple initialization tasks
+ * that can run in parallel (i.e. that do not depend on each other). This class
+ * takes care about the management of an {@code ExecutorService} and shares it
+ * with the {@code BackgroundInitializer} objects it is responsible for; so the
+ * using application need not bother with these details.
+ * </p>
+ * <p>
+ * The typical usage scenario for {@code MultiBackgroundInitializer} is as
+ * follows:
+ * <ul>
+ * <li>Create a new instance of the class. Optionally pass in a pre-configured
+ * {@code ExecutorService}. Alternatively {@code MultiBackgroundInitializer} can
+ * create a temporary {@code ExecutorService} and delete it after initialization
+ * is complete.</li>
+ * <li>Create specialized {@link BackgroundInitializer} objects for the
+ * initialization tasks to be performed and add them to the {@code
+ * MultiBackgroundInitializer} using the
+ * {@link #addInitializer(String, BackgroundInitializer)} method.</li>
+ * <li>After all initializers have been added, call the {@link #start()} method.
+ * </li>
+ * <li>When access to the result objects produced by the {@code
+ * BackgroundInitializer} objects is needed call the {@link #get()} method. The
+ * object returned here provides access to all result objects created during
+ * initialization. It also stores information about exceptions that have
+ * occurred.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * {@code MultiBackgroundInitializer} starts a special controller task that
+ * starts all {@code BackgroundInitializer} objects added to the instance.
+ * Before the an initializer is started it is checked whether this initializer
+ * already has an {@code ExecutorService} set. If this is the case, this {@code
+ * ExecutorService} is used for running the background task. Otherwise the
+ * current {@code ExecutorService} of this {@code MultiBackgroundInitializer} is
+ * shared with the initializer.
+ * </p>
+ * <p>
+ * The easiest way of using this class is to let it deal with the management of
+ * an {@code ExecutorService} itself: If no external {@code ExecutorService} is
+ * provided, the class creates a temporary {@code ExecutorService} (that is
+ * capable of executing all background tasks in parallel) and destroys it at the
+ * end of background processing.
+ * </p>
+ * <p>
+ * Alternatively an external {@code ExecutorService} can be provided - either at
+ * construction time or later by calling the
+ * {@link #setExternalExecutor(ExecutorService)} method. In this case all
+ * background tasks are scheduled at this external {@code ExecutorService}.
+ * <strong>Important note:</strong> When using an external {@code
+ * ExecutorService} be sure that the number of threads managed by the service is
+ * large enough. Otherwise a deadlock can happen! This is the case in the
+ * following scenario: {@code MultiBackgroundInitializer} starts a task that
+ * starts all registered {@code BackgroundInitializer} objects and waits for
+ * their completion. If for instance a single threaded {@code ExecutorService}
+ * is used, none of the background tasks can be executed, and the task created
+ * by {@code MultiBackgroundInitializer} waits forever.
+ * </p>
+ *
+ * @version $Id$
+ */
+public class MultiBackgroundInitializer
+        extends
+        BackgroundInitializer<MultiBackgroundInitializer.MultiBackgroundInitializerResults>
{
+    /** A map with the child initializers. */
+    private final Map<String, BackgroundInitializer<?>> childInitializers = new
HashMap<String, BackgroundInitializer<?>>();
+
+    /**
+     * Creates a new instance of {@code MultiBackgroundInitializer}.
+     */
+    public MultiBackgroundInitializer() {
+        super();
+    }
+
+    /**
+     * Creates a new instance of {@code MultiBackgroundInitializer} and
+     * initializes it with the given external {@code ExecutorService}.
+     *
+     * @param exec the {@code ExecutorService} for executing the background
+     * tasks
+     */
+    public MultiBackgroundInitializer(ExecutorService exec) {
+        super(exec);
+    }
+
+    /**
+     * Adds a new {@code BackgroundInitializer} to this object. When this
+     * {@code MultiBackgroundInitializer} is started, the given initializer will
+     * be processed. This method must not be called after {@link #start()} has
+     * been invoked.
+     *
+     * @param name the name of the initializer (must not be <b>null</b>)
+     * @param init the {@code BackgroundInitializer} to add (must not be
+     * <b>null</b>)
+     * @throws IllegalArgumentException if a required parameter is missing
+     * @throws IllegalStateException if {@code start()} has already been called
+     */
+    public void addInitializer(String name, BackgroundInitializer<?> init) {
+        if (name == null) {
+            throw new IllegalArgumentException(
+                    "Name of child initializer must not be null!");
+        }
+        if (init == null) {
+            throw new IllegalArgumentException(
+                    "Child initializer must not be null!");
+        }
+
+        synchronized (this) {
+            if (isStarted()) {
+                throw new IllegalStateException(
+                        "addInitializer() must not be called after start()!");
+            }
+            childInitializers.put(name, init);
+        }
+    }
+
+    /**
+     * Returns the number of tasks needed for executing all child {@code
+     * BackgroundInitializer} objects in parallel. This implementation sums up
+     * the required tasks for all child initializers (which is necessary if one
+     * of the child initializers is itself a {@code MultiBackgroundInitializer}
+     * ). Then it adds 1 for the control task that waits for the completion of
+     * the children.
+     *
+     * @return the number of tasks required for background processing
+     */
+    @Override
+    protected int getTaskCount() {
+        int result = 1;
+
+        for (BackgroundInitializer<?> bi : childInitializers.values()) {
+            result += bi.getTaskCount();
+        }
+
+        return result;
+    }
+
+    /**
+     * Creates the results object. This implementation starts all child {@code
+     * BackgroundInitializer} objects. Then it collects their results and
+     * creates a {@code MultiBackgroundInitializerResults} object with this
+     * data. If a child initializer throws a checked exceptions, it is added to
+     * the results object. Unchecked exceptions are propagated.
+     *
+     * @return the results object
+     * @throws Exception if an error occurs
+     */
+    @Override
+    protected MultiBackgroundInitializerResults initialize() throws Exception {
+        Map<String, BackgroundInitializer<?>> inits;
+        synchronized (this) {
+            // create a snapshot to operate on
+            inits = new HashMap<String, BackgroundInitializer<?>>(
+                    childInitializers);
+        }
+
+        // start the child initializers
+        ExecutorService exec = getActiveExecutor();
+        for (BackgroundInitializer<?> bi : inits.values()) {
+            if (bi.getExternalExecutor() == null) {
+                // share the executor service if necessary
+                bi.setExternalExecutor(exec);
+            }
+            bi.start();
+        }
+
+        // collect the results
+        Map<String, Object> results = new HashMap<String, Object>();
+        Map<String, ConcurrentException> excepts = new HashMap<String, ConcurrentException>();
+        for (Map.Entry<String, BackgroundInitializer<?>> e : inits.entrySet())
{
+            try {
+                results.put(e.getKey(), e.getValue().get());
+            } catch (ConcurrentException cex) {
+                excepts.put(e.getKey(), cex);
+            }
+        }
+
+        return new MultiBackgroundInitializerResults(inits, results, excepts);
+    }
+
+    /**
+     * A data class for storing the results of the background initialization
+     * performed by {@code MultiBackgroundInitializer}. Objects of this inner
+     * class are returned by {@link MultiBackgroundInitializer#initialize()}.
+     * They allow access to all result objects produced by the
+     * {@link BackgroundInitializer} objects managed by the owning instance. It
+     * is also possible to retrieve status information about single
+     * {@link BackgroundInitializer}s, i.e. whether they completed normally or
+     * caused an exception.
+     */
+    public static class MultiBackgroundInitializerResults {
+        /** A map with the child initializers. */
+        private final Map<String, BackgroundInitializer<?>> initializers;
+
+        /** A map with the result objects. */
+        private final Map<String, Object> resultObjects;
+
+        /** A map with the exceptions. */
+        private final Map<String, ConcurrentException> exceptions;
+
+        /**
+         * Creates a new instance of {@code MultiBackgroundInitializerResults}
+         * and initializes it with maps for the {@code BackgroundInitializer}
+         * objects, their result objects and the exceptions thrown by them.
+         *
+         * @param inits the {@code BackgroundInitializer} objects
+         * @param results the result objects
+         * @param excepts the exceptions
+         */
+        private MultiBackgroundInitializerResults(
+                Map<String, BackgroundInitializer<?>> inits,
+                Map<String, Object> results,
+                Map<String, ConcurrentException> excepts) {
+            initializers = inits;
+            resultObjects = results;
+            exceptions = excepts;
+        }
+
+        /**
+         * Returns the {@code BackgroundInitializer} with the given name. If the
+         * name cannot be resolved, an exception is thrown.
+         *
+         * @param name the name of the {@code BackgroundInitializer}
+         * @return the {@code BackgroundInitializer} with this name
+         * @throws NoSuchElementException if the name cannot be resolved
+         */
+        public BackgroundInitializer<?> getInitializer(String name) {
+            return checkName(name);
+        }
+
+        /**
+         * Returns the result object produced by the {@code
+         * BackgroundInitializer} with the given name. This is the object
+         * returned by the initializer's {@code initialize()} method. If this
+         * {@code BackgroundInitializer} caused an exception, <b>null</b> is
+         * returned. If the name cannot be resolved, an exception is thrown.
+         *
+         * @param name the name of the {@code BackgroundInitializer}
+         * @return the result object produced by this {@code
+         * BackgroundInitializer}
+         * @throws NoSuchElementException if the name cannot be resolved
+         */
+        public Object getResultObject(String name) {
+            checkName(name);
+            return resultObjects.get(name);
+        }
+
+        /**
+         * Returns a flag whether the {@code BackgroundInitializer} with the
+         * given name caused an exception.
+         *
+         * @param name the name of the {@code BackgroundInitializer}
+         * @return a flag whether this initializer caused an exception
+         * @throws NoSuchElementException if the name cannot be resolved
+         */
+        public boolean isException(String name) {
+            checkName(name);
+            return exceptions.containsKey(name);
+        }
+
+        /**
+         * Returns the {@code ConcurrentException} object that was thrown by the
+         * {@code BackgroundInitializer} with the given name. If this
+         * initializer did not throw an exception, the return value is
+         * <b>null</b>. If the name cannot be resolved, an exception is thrown.
+         *
+         * @param name the name of the {@code BackgroundInitializer}
+         * @return the exception thrown by this initializer
+         * @throws NoSuchElementException if the name cannot be resolved
+         */
+        public ConcurrentException getException(String name) {
+            checkName(name);
+            return exceptions.get(name);
+        }
+
+        /**
+         * Returns a set with the names of all {@code BackgroundInitializer}
+         * objects managed by the {@code MultiBackgroundInitializer}.
+         *
+         * @return an (unmodifiable) set with the names of the managed {@code
+         * BackgroundInitializer} objects
+         */
+        public Set<String> initializerNames() {
+            return Collections.unmodifiableSet(initializers.keySet());
+        }
+
+        /**
+         * Checks whether an initializer with the given name exists. If not,
+         * throws an exception. If it exists, the associated child initializer
+         * is returned.
+         *
+         * @param name the name to check
+         * @return the initializer with this name
+         * @throws NoSuchElementException if the name is unknown
+         */
+        private BackgroundInitializer<?> checkName(String name) {
+            BackgroundInitializer<?> init = initializers.get(name);
+            if (init == null) {
+                throw new NoSuchElementException(
+                        "No child initializer with name " + name);
+            }
+
+            return init;
+        }
+    }
+}

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/MultiBackgroundInitializer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java?rev=831587&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
(added)
+++ commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
Sat Oct 31 19:52:58 2009
@@ -0,0 +1,364 @@
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import junit.framework.TestCase;
+
+/**
+ * Test class for {@link MultiBackgroundInitializer}.
+ *
+ * @version $Id$
+ */
+public class MultiBackgroundInitializerTest extends TestCase {
+    /** Constant for the names of the child initializers. */
+    private static final String CHILD_INIT = "childInitializer";
+
+    /** The initializer to be tested. */
+    private MultiBackgroundInitializer initializer;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        initializer = new MultiBackgroundInitializer();
+    }
+
+    /**
+     * Tests whether a child initializer has been executed. Optionally the
+     * expected executor service can be checked, too.
+     *
+     * @param child the child initializer
+     * @param expExec the expected executor service (null if the executor should
+     * not be checked)
+     * @throws ConcurrentException if an error occurs
+     */
+    private void checkChild(BackgroundInitializer<?> child,
+            ExecutorService expExec) throws ConcurrentException {
+        ChildBackgroundInitializer cinit = (ChildBackgroundInitializer) child;
+        Integer result = cinit.get();
+        assertEquals("Wrong result", 1, result.intValue());
+        assertEquals("Wrong number of executions", 1, cinit.initializeCalls);
+        if (expExec != null) {
+            assertEquals("Wrong executor service", expExec,
+                    cinit.currentExecutor);
+        }
+    }
+
+    /**
+     * Tests addInitializer() if a null name is passed in. This should cause an
+     * exception.
+     */
+    public void testAddInitializerNullName() {
+        try {
+            initializer.addInitializer(null, new ChildBackgroundInitializer());
+            fail("Null name not detected!");
+        } catch (IllegalArgumentException iex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tests addInitializer() if a null initializer is passed in. This should
+     * cause an exception.
+     */
+    public void testAddInitializerNullInit() {
+        try {
+            initializer.addInitializer(CHILD_INIT, null);
+            fail("Could add null initializer!");
+        } catch (IllegalArgumentException iex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tests the background processing if there are no child initializers.
+     */
+    public void testInitializeNoChildren() throws ConcurrentException {
+        assertTrue("Wrong result of start()", initializer.start());
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
+                .get();
+        assertTrue("Got child initializers", res.initializerNames().isEmpty());
+        assertTrue("Executor not shutdown", initializer.getActiveExecutor()
+                .isShutdown());
+    }
+
+    /**
+     * Helper method for testing the initialize() method. This method can
+     * operate with both an external and a temporary executor service.
+     *
+     * @return the result object produced by the initializer
+     */
+    private MultiBackgroundInitializer.MultiBackgroundInitializerResults checkInitialize()
+            throws ConcurrentException {
+        final int count = 5;
+        for (int i = 0; i < count; i++) {
+            initializer.addInitializer(CHILD_INIT + i,
+                    new ChildBackgroundInitializer());
+        }
+        initializer.start();
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
+                .get();
+        assertEquals("Wrong number of child initializers", count, res
+                .initializerNames().size());
+        for (int i = 0; i < count; i++) {
+            String key = CHILD_INIT + i;
+            assertTrue("Name not found: " + key, res.initializerNames()
+                    .contains(key));
+            assertEquals("Wrong result object", Integer.valueOf(1), res
+                    .getResultObject(key));
+            assertFalse("Exception flag", res.isException(key));
+            assertNull("Got an exception", res.getException(key));
+            checkChild(res.getInitializer(key), initializer.getActiveExecutor());
+        }
+        return res;
+    }
+
+    /**
+     * Tests background processing if a temporary executor is used.
+     */
+    public void testInitializeTempExec() throws ConcurrentException {
+        checkInitialize();
+        assertTrue("Executor not shutdown", initializer.getActiveExecutor()
+                .isShutdown());
+    }
+
+    /**
+     * Tests background processing if an external executor service is provided.
+     */
+    public void testInitializeExternalExec() throws ConcurrentException {
+        ExecutorService exec = Executors.newCachedThreadPool();
+        try {
+            initializer = new MultiBackgroundInitializer(exec);
+            checkInitialize();
+            assertEquals("Wrong executor", exec, initializer
+                    .getActiveExecutor());
+            assertFalse("Executor was shutdown", exec.isShutdown());
+        } finally {
+            exec.shutdown();
+        }
+    }
+
+    /**
+     * Tests the behavior of initialize() if a child initializer has a specific
+     * executor service. Then this service should not be overridden.
+     */
+    public void testInitializeChildWithExecutor() throws ConcurrentException {
+        final String initExec = "childInitializerWithExecutor";
+        ExecutorService exec = Executors.newSingleThreadExecutor();
+        try {
+            ChildBackgroundInitializer c1 = new ChildBackgroundInitializer();
+            ChildBackgroundInitializer c2 = new ChildBackgroundInitializer();
+            c2.setExternalExecutor(exec);
+            initializer.addInitializer(CHILD_INIT, c1);
+            initializer.addInitializer(initExec, c2);
+            initializer.start();
+            initializer.get();
+            checkChild(c1, initializer.getActiveExecutor());
+            checkChild(c2, exec);
+        } finally {
+            exec.shutdown();
+        }
+    }
+
+    /**
+     * Tries to add another child initializer after the start() method has been
+     * called. This should not be allowed.
+     */
+    public void testAddInitializerAfterStart() throws ConcurrentException {
+        initializer.start();
+        try {
+            initializer.addInitializer(CHILD_INIT,
+                    new ChildBackgroundInitializer());
+            fail("Could add initializer after start()!");
+        } catch (IllegalStateException istex) {
+            initializer.get();
+        }
+    }
+
+    /**
+     * Tries to query an unknown child initializer from the results object. This
+     * should cause an exception.
+     */
+    public void testResultGetInitializerUnknown() throws ConcurrentException {
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
+        try {
+            res.getInitializer("unknown");
+            fail("Could obtain unknown child initializer!");
+        } catch (NoSuchElementException nex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tries to query the results of an unknown child initializer from the
+     * results object. This should cause an exception.
+     */
+    public void testResultGetResultObjectUnknown() throws ConcurrentException {
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
+        try {
+            res.getResultObject("unknown");
+            fail("Could obtain results from unknown child initializer!");
+        } catch (NoSuchElementException nex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tries to query the exception of an unknown child initializer from the
+     * results object. This should cause an exception.
+     */
+    public void testResultGetExceptionUnknown() throws ConcurrentException {
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
+        try {
+            res.getException("unknown");
+            fail("Could obtain exception from unknown child initializer!");
+        } catch (NoSuchElementException nex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tries to query the exception flag of an unknown child initializer from
+     * the results object. This should cause an exception.
+     */
+    public void testResultIsExceptionUnknown() throws ConcurrentException {
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
+        try {
+            res.isException("unknown");
+            fail("Could obtain exception status from unknown child initializer!");
+        } catch (NoSuchElementException nex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tests that the set with the names of the initializers cannot be modified.
+     */
+    public void testResultInitializerNamesModify() throws ConcurrentException {
+        checkInitialize();
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
+                .get();
+        Iterator<String> it = res.initializerNames().iterator();
+        it.next();
+        try {
+            it.remove();
+            fail("Could modify set with initializer names!");
+        } catch (UnsupportedOperationException uex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tests the behavior of the initializer if one of the child initializers
+     * throws a runtime exception.
+     */
+    public void testInitializeRuntimeEx() throws ConcurrentException {
+        ChildBackgroundInitializer child = new ChildBackgroundInitializer();
+        child.ex = new RuntimeException();
+        initializer.addInitializer(CHILD_INIT, child);
+        initializer.start();
+        try {
+            initializer.get();
+            fail("Runtime exception not thrown!");
+        } catch (Exception ex) {
+            assertEquals("Wrong exception", child.ex, ex);
+        }
+    }
+
+    /**
+     * Tests the behavior of the initializer if one of the child initializers
+     * throws a checked exception.
+     */
+    public void testInitializeEx() throws ConcurrentException {
+        ChildBackgroundInitializer child = new ChildBackgroundInitializer();
+        child.ex = new Exception();
+        initializer.addInitializer(CHILD_INIT, child);
+        initializer.start();
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
+                .get();
+        assertTrue("No exception flag", res.isException(CHILD_INIT));
+        assertNull("Got a results object", res.getResultObject(CHILD_INIT));
+        ConcurrentException cex = res.getException(CHILD_INIT);
+        assertEquals("Wrong cause", child.ex, cex.getCause());
+    }
+
+    /**
+     * Tests whether MultiBackgroundInitializers can be combined in a nested
+     * way.
+     */
+    public void testInitializeNested() throws ConcurrentException {
+        final String nameMulti = "multiChildInitializer";
+        initializer
+                .addInitializer(CHILD_INIT, new ChildBackgroundInitializer());
+        MultiBackgroundInitializer mi2 = new MultiBackgroundInitializer();
+        final int count = 3;
+        for (int i = 0; i < count; i++) {
+            mi2
+                    .addInitializer(CHILD_INIT + i,
+                            new ChildBackgroundInitializer());
+        }
+        initializer.addInitializer(nameMulti, mi2);
+        initializer.start();
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
+                .get();
+        ExecutorService exec = initializer.getActiveExecutor();
+        checkChild(res.getInitializer(CHILD_INIT), exec);
+        MultiBackgroundInitializer.MultiBackgroundInitializerResults res2 = (MultiBackgroundInitializer.MultiBackgroundInitializerResults)
res
+                .getResultObject(nameMulti);
+        assertEquals("Wrong number of initializers", count, res2
+                .initializerNames().size());
+        for (int i = 0; i < count; i++) {
+            checkChild(res2.getInitializer(CHILD_INIT + i), exec);
+        }
+        assertTrue("Executor not shutdown", exec.isShutdown());
+    }
+
+    /**
+     * A concrete implementation of {@code BackgroundInitializer} used for
+     * defining background tasks for {@code MultiBackgroundInitializer}.
+     */
+    private static class ChildBackgroundInitializer extends
+            BackgroundInitializer<Integer> {
+        /** Stores the current executor service. */
+        volatile ExecutorService currentExecutor;
+
+        /** A counter for the invocations of initialize(). */
+        volatile int initializeCalls;
+
+        /** An exception to be thrown by initialize(). */
+        Exception ex;
+
+        /**
+         * Records this invocation. Optionally throws an exception.
+         */
+        @Override
+        protected Integer initialize() throws Exception {
+            currentExecutor = getActiveExecutor();
+            initializeCalls++;
+
+            if (ex != null) {
+                throw ex;
+            }
+
+            return initializeCalls;
+        }
+    }
+}

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/MultiBackgroundInitializerTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message