2015-02-25 15:20 GMT+01:00 Benedikt Ritter <brit...@apache.org>:

>
>
> 2015-02-23 21:47 GMT+01:00 Oliver Heger <oliver.he...@oliver-heger.de>:
>
>>
>>
>> Am 23.02.2015 um 21:35 schrieb Benedikt Ritter:
>> > Oliver Heger has raised concerns about this commit in JIRA [1]:
>> >
>> >> This is a strong change in the behavior of this class. The main
>> property
>> > of atomic initializers was that they are non
>> >> blocking. Now a blocking wait has been introduced. When there is so
>> much
>> > contention that the busy wait is
>> >> actually a problem, wouln't it then be better to directly use a
>> blocking
>> > variant like lazy initializer?
>> >
>> > I've looked through the JavaDoc of AtomicInitializer once more. It says:
>> > "Because {@code AtomicSafeInitializer} does not use synchronization at
>> all
>> > it probably outruns {@link LazyInitializer}, at least under low or
>> moderate
>> > concurrent access."
>> >
>> > This is the only thing I can find regarding concurrency properties of
>> > AtomicInitializer. I think this still holds, doesn't it?
>>
>> No, the CountDownLatch is synchronized.
>>
>> There are multiple initializer implementations with different properties
>> which are suitable for different use cases and scenarios. The atomic
>> initializers are useful if you want to avoid blocking calls, but they
>> might be an inferior solution under high contention.
>>
>> My fear is that this commit optimizes the class for a use case which can
>> be served better by another initializer implementation which is already
>> blocking; and this optimization has negative impact on the original
>> intention of AtomicSafeInitializer.
>>
>
> Okay. Looks like we should address this intend better in the JavaDoc of
> AtomicSafeInitializer. Maybe it should even have a better name like
> BusyWaitInitializer (could be changed for lang4).
>
> Do you think the changes made to AtomicSafeInitializer are an optimization
> that would better fit into LazyInitializer?
>
> I'll revert this change as soon as I get the time.
>

The change has been reverted in revision 1662379.


>
> Benedikt
>
>
>>
>> Oliver
>>
>> >
>> > Benedikt
>> >
>> > [1] https://issues.apache.org/jira/browse/LANG-1086
>> >
>> > 2015-02-23 21:15 GMT+01:00 <brit...@apache.org>:
>> >
>> >> Author: britter
>> >> Date: Mon Feb 23 20:15:49 2015
>> >> New Revision: 1661762
>> >>
>> >> URL: http://svn.apache.org/r1661762
>> >> Log:
>> >> LANG-1086: Remove busy wait from AtomicSafeInitializer.get(). This also
>> >> fixes #46 from github. Thanks to github user artnaseef.
>> >>
>> >> Modified:
>> >>     commons/proper/lang/trunk/src/changes/changes.xml
>> >>
>> >>
>> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
>> >>
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
>> >>
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
>> >>
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
>> >>
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
>> >>
>> >> Modified: commons/proper/lang/trunk/src/changes/changes.xml
>> >> URL:
>> >>
>> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/changes/changes.xml?rev=1661762&r1=1661761&r2=1661762&view=diff
>> >>
>> >>
>> ==============================================================================
>> >> --- commons/proper/lang/trunk/src/changes/changes.xml [utf-8]
>> (original)
>> >> +++ commons/proper/lang/trunk/src/changes/changes.xml [utf-8] Mon Feb
>> 23
>> >> 20:15:49 2015
>> >> @@ -22,6 +22,7 @@
>> >>    <body>
>> >>
>> >>    <release version="3.4" date="tba" description="tba">
>> >> +    <action issue="LANG-1086" type="update" dev="britter">Remove busy
>> >> wait from AtomicSafeInitializer.get()</action>
>> >>      <action issue="LANG-1081" type="fix" dev="britter"
>> due-to="Jonathan
>> >> Baker">DiffBuilder.append(String, Object left, Object right) does not
>> do a
>> >> left.equals(right) check</action>
>> >>      <action issue="LANG-1055" type="fix" dev="britter"
>> due-to="Jonathan
>> >> Baker">StrSubstitutor.replaceSystemProperties does not work
>> >> consistently</action>
>> >>      <action issue="LANG-1082" type="add" dev="britter"
>> due-to="Jonathan
>> >> Baker">Add option to disable the "objectsTriviallyEqual" test in
>> >> DiffBuilder</action>
>> >>
>> >> Modified:
>> >>
>> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
>> >> URL:
>> >>
>> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java?rev=1661762&r1=1661761&r2=1661762&view=diff
>> >>
>> >>
>> ==============================================================================
>> >> ---
>> >>
>> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
>> >> (original)
>> >> +++
>> >>
>> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
>> >> Mon Feb 23 20:15:49 2015
>> >> @@ -16,6 +16,7 @@
>> >>   */
>> >>  package org.apache.commons.lang3.concurrent;
>> >>
>> >> +import java.util.concurrent.CountDownLatch;
>> >>  import java.util.concurrent.atomic.AtomicReference;
>> >>
>> >>  /**
>> >> @@ -62,20 +63,44 @@ public abstract class AtomicSafeInitiali
>> >>      /** Holds the reference to the managed object. */
>> >>      private final AtomicReference<T> reference = new
>> AtomicReference<T>();
>> >>
>> >> +    /** Holds the exception that terminated the initialize() method,
>> if
>> >> an exception was thrown */
>> >> +    private final AtomicReference<ConcurrentException> referenceExc =
>> new
>> >> AtomicReference<ConcurrentException>();
>> >> +
>> >> +    /** Latch for those threads waiting for initialization to
>> complete. */
>> >> +    private final CountDownLatch latch = new CountDownLatch(1);
>> >> +
>> >>      /**
>> >>       * Get (and initialize, if not initialized yet) the required
>> object
>> >>       *
>> >>       * @return lazily initialized object
>> >>       * @throws ConcurrentException if the initialization of the object
>> >> causes an
>> >> -     * exception
>> >> +     * exception or the thread is interrupted waiting for another
>> thread
>> >> to
>> >> +     * complete the initialization
>> >>       */
>> >>      @Override
>> >>      public final T get() throws ConcurrentException {
>> >>          T result;
>> >>
>> >> -        while ((result = reference.get()) == null) {
>> >> +        if ((result = reference.get()) == null) {
>> >>              if (factory.compareAndSet(null, this)) {
>> >> -                reference.set(initialize());
>> >> +                try {
>> >> +                    reference.set(result = initialize());
>> >> +                } catch ( ConcurrentException exc ) {
>> >> +                    referenceExc.set(exc);
>> >> +                    throw exc;
>> >> +                } finally {
>> >> +                    latch.countDown();
>> >> +                }
>> >> +            } else {
>> >> +                try {
>> >> +                    latch.await();
>> >> +                    if ( referenceExc.get() != null ) {
>> >> +                        throw new
>> >> ConcurrentException(referenceExc.get().getMessage(),
>> >> referenceExc.get().getCause());
>> >> +                    }
>> >> +                    result = reference.get();
>> >> +                } catch (InterruptedException intExc) {
>> >> +                    throw new ConcurrentException("interrupted waiting
>> >> for initialization to complete", intExc);
>> >> +                }
>> >>              }
>> >>          }
>> >>
>> >>
>> >> Modified:
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
>> >> URL:
>> >>
>> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
>> >>
>> >>
>> ==============================================================================
>> >> ---
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
>> >> (original)
>> >> +++
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
>> >> Mon Feb 23 20:15:49 2015
>> >> @@ -18,6 +18,8 @@ package org.apache.commons.lang3.concurr
>> >>
>> >>  import static org.junit.Assert.assertEquals;
>> >>  import static org.junit.Assert.assertNotNull;
>> >> +import static org.junit.Assert.assertSame;
>> >> +import static org.junit.Assert.assertTrue;
>> >>
>> >>  import java.util.concurrent.CountDownLatch;
>> >>
>> >> @@ -72,7 +74,41 @@ public abstract class AbstractConcurrent
>> >>      @Test
>> >>      public void testGetConcurrent() throws ConcurrentException,
>> >>              InterruptedException {
>> >> -        final ConcurrentInitializer<Object> initializer =
>> >> createInitializer();
>> >> +
>> >> +        this.testGetConcurrentOptionallyWithException(false, null,
>> null);
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * Tests the handling of exceptions thrown on the initialized when
>> >> multiple threads execute concurrently.
>> >> +     * Always an exception with the same message and cause should be
>> >> thrown.
>> >> +     *
>> >> +     * @throws org.apache.commons.lang3.concurrent.ConcurrentException
>> >> because the object under test may throw it
>> >> +     * @throws java.lang.InterruptedException because the threading
>> API
>> >> my throw it
>> >> +     */
>> >> +    public void testGetConcurrentWithException(String expectedMessage,
>> >> +                                               Exception
>> expectedCause)
>> >> +            throws ConcurrentException, InterruptedException {
>> >> +
>> >> +        this.testGetConcurrentOptionallyWithException(true,
>> >> expectedMessage, expectedCause);
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * Tests whether get() can be invoked from multiple threads
>> >> concurrently.  Supports the exception-handling case
>> >> +     * and the normal, non-exception case.
>> >> +     *
>> >> +     * Always the same object should be returned, or an exception with
>> >> the same message and cause should be thrown.
>> >> +     *
>> >> +     * @throws org.apache.commons.lang3.concurrent.ConcurrentException
>> >> because the object under test may throw it
>> >> +     * @throws java.lang.InterruptedException because the threading
>> API
>> >> my throw it
>> >> +     */
>> >> +    protected void testGetConcurrentOptionallyWithException(boolean
>> >> expectExceptions, String expectedMessage,
>> >> +                                                            Exception
>> >> expectedCause)
>> >> +            throws ConcurrentException, InterruptedException {
>> >> +
>> >> +        final ConcurrentInitializer<Object> initializer =
>> >> expectExceptions ?
>> >> +                createExceptionThrowingInitializer() :
>> >> +                createInitializer();
>> >> +
>> >>          final int threadCount = 20;
>> >>          final CountDownLatch startLatch = new CountDownLatch(1);
>> >>          class GetThread extends Thread {
>> >> @@ -106,9 +142,18 @@ public abstract class AbstractConcurrent
>> >>          }
>> >>
>> >>          // check results
>> >> -        final Object managedObject = initializer.get();
>> >> -        for (final GetThread t : threads) {
>> >> -            assertEquals("Wrong object", managedObject, t.object);
>> >> +        if ( expectExceptions ) {
>> >> +            for (GetThread t : threads) {
>> >> +                assertTrue(t.object instanceof Exception);
>> >> +                Exception exc = (Exception) t.object;
>> >> +                assertEquals(expectedMessage, exc.getMessage());
>> >> +                assertSame(expectedCause, exc.getCause());
>> >> +            }
>> >> +        } else {
>> >> +            final Object managedObject = initializer.get();
>> >> +            for (final GetThread t : threads) {
>> >> +                assertEquals("Wrong object", managedObject, t.object);
>> >> +            }
>> >>          }
>> >>      }
>> >>
>> >> @@ -119,4 +164,12 @@ public abstract class AbstractConcurrent
>> >>       * @return the initializer object to be tested
>> >>       */
>> >>      protected abstract ConcurrentInitializer<Object>
>> createInitializer();
>> >> +
>> >> +    /**
>> >> +     * Creates a {@link ConcurrentInitializer} object that always
>> throws
>> >> +     * exceptions.
>> >> +     *
>> >> +     * @return
>> >> +     */
>> >> +    protected abstract ConcurrentInitializer<Object>
>> >> createExceptionThrowingInitializer();
>> >>  }
>> >>
>> >> Modified:
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
>> >> URL:
>> >>
>> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
>> >>
>> >>
>> ==============================================================================
>> >> ---
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
>> >> (original)
>> >> +++
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
>> >> Mon Feb 23 20:15:49 2015
>> >> @@ -16,12 +16,29 @@
>> >>   */
>> >>  package org.apache.commons.lang3.concurrent;
>> >>
>> >> +import org.junit.Test;
>> >> +
>> >>  /**
>> >>   * Test class for {@code AtomicInitializer}.
>> >>   *
>> >>   * @version $Id$
>> >>   */
>> >>  public class AtomicInitializerTest extends
>> >> AbstractConcurrentInitializerTest {
>> >> +    private Exception testCauseException;
>> >> +    private String testExceptionMessage;
>> >> +
>> >> +    public AtomicInitializerTest() {
>> >> +        testExceptionMessage = "x-test-exception-message-x";
>> >> +        testCauseException = new Exception(testExceptionMessage);
>> >> +    }
>> >> +
>> >> +    @Test
>> >> +    public void testGetConcurrentWithException ()
>> >> +            throws ConcurrentException, InterruptedException {
>> >> +
>> >> +        super.testGetConcurrentWithException(testExceptionMessage,
>> >> testCauseException);
>> >> +    }
>> >> +
>> >>      /**
>> >>       * Returns the initializer to be tested.
>> >>       *
>> >> @@ -36,4 +53,20 @@ public class AtomicInitializerTest exten
>> >>              }
>> >>          };
>> >>      }
>> >> +
>> >> +    @Override
>> >> +    protected ConcurrentInitializer<Object>
>> >> createExceptionThrowingInitializer() {
>> >> +        return new ExceptionThrowingAtomicSafeInitializerTestImpl();
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * A concrete test implementation of {@code
>> AtomicSafeInitializer}.
>> >> This
>> >> +     * implementation always throws an exception.
>> >> +     */
>> >> +    private class ExceptionThrowingAtomicSafeInitializerTestImpl
>> extends
>> >> AtomicSafeInitializer<Object> {
>> >> +        @Override
>> >> +        protected Object initialize() throws ConcurrentException {
>> >> +            throw new ConcurrentException(testExceptionMessage,
>> >> testCauseException);
>> >> +        }
>> >> +    }
>> >>  }
>> >>
>> >> Modified:
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
>> >> URL:
>> >>
>> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
>> >>
>> >>
>> ==============================================================================
>> >> ---
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
>> >> (original)
>> >> +++
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
>> >> Mon Feb 23 20:15:49 2015
>> >> @@ -17,7 +17,11 @@
>> >>  package org.apache.commons.lang3.concurrent;
>> >>
>> >>  import static org.junit.Assert.assertEquals;
>> >> +import static org.junit.Assert.assertFalse;
>> >> +import static org.junit.Assert.assertSame;
>> >> +import static org.junit.Assert.assertTrue;
>> >>
>> >> +import java.util.concurrent.CountDownLatch;
>> >>  import java.util.concurrent.atomic.AtomicInteger;
>> >>
>> >>  import org.junit.Before;
>> >> @@ -30,12 +34,19 @@ import org.junit.Test;
>> >>   */
>> >>  public class AtomicSafeInitializerTest extends
>> >>          AbstractConcurrentInitializerTest {
>> >> +
>> >>      /** The instance to be tested. */
>> >>      private AtomicSafeInitializerTestImpl initializer;
>> >> +    private ExceptionThrowingAtomicSafeInitializerTestImpl
>> >> exceptionThrowingInitializer;
>> >> +    private Exception testCauseException;
>> >> +    private String testExceptionMessage;
>> >>
>> >>      @Before
>> >>      public void setUp() throws Exception {
>> >>          initializer = new AtomicSafeInitializerTestImpl();
>> >> +        exceptionThrowingInitializer = new
>> >> ExceptionThrowingAtomicSafeInitializerTestImpl();
>> >> +        testExceptionMessage = "x-test-exception-message-x";
>> >> +        testCauseException = new Exception(testExceptionMessage);
>> >>      }
>> >>
>> >>      /**
>> >> @@ -49,6 +60,17 @@ public class AtomicSafeInitializerTest e
>> >>      }
>> >>
>> >>      /**
>> >> +     * Returns the exception-throwing initializer to be tested.
>> >> +     *
>> >> +     * @return the {@code AtomicSafeInitializer} under test when
>> >> validating
>> >> +     * exception handling
>> >> +     */
>> >> +    @Override
>> >> +    protected ConcurrentInitializer<Object>
>> >> createExceptionThrowingInitializer() {
>> >> +        return exceptionThrowingInitializer;
>> >> +    }
>> >> +
>> >> +    /**
>> >>       * Tests that initialize() is called only once.
>> >>       *
>> >>       * @throws org.apache.commons.lang3.concurrent.ConcurrentException
>> >> because {@link #testGetConcurrent()} may throw it
>> >> @@ -62,6 +84,92 @@ public class AtomicSafeInitializerTest e
>> >>                  initializer.initCounter.get());
>> >>      }
>> >>
>> >> +    @Test
>> >> +    public void testExceptionOnInitialize() throws
>> ConcurrentException,
>> >> +            InterruptedException {
>> >> +
>> >> +        testGetConcurrentWithException(testExceptionMessage,
>> >> testCauseException);
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * Validate the handling of an interrupted exception on a thread
>> >> waiting for another thread to finish calling the
>> >> +     * initialize() method.
>> >> +     *
>> >> +     * @throws Exception
>> >> +     */
>> >> +    @Test(timeout = 3000)
>> >> +    public void testInterruptedWaitingOnInitialize() throws Exception
>> {
>> >> +        this.execTestWithWaitingOnInitialize(true);
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * Test the success path of two threads reaching the
>> initialization
>> >> point at the same time.
>> >> +     */
>> >> +    @Test(timeout = 3000)
>> >> +    public void testOneThreadWaitingForAnotherToInitialize () throws
>> >> Exception {
>> >> +        execTestWithWaitingOnInitialize(false);
>> >> +    }
>> >> +
>> >> +
>> >> +    /**
>> >> +     * Execute a test that requires one thread to be waiting on the
>> >> initialize() method of another thread.  This test
>> >> +     * uses latches to guarantee the code path being tested.
>> >> +     *
>> >> +     * @throws Exception
>> >> +     */
>> >> +    protected void execTestWithWaitingOnInitialize(boolean
>> interruptInd)
>> >> throws Exception {
>> >> +        final CountDownLatch startLatch = new CountDownLatch(1);
>> >> +        final CountDownLatch finishLatch = new CountDownLatch(1);
>> >> +        final WaitingInitializerTestImpl initializer = new
>> >> WaitingInitializerTestImpl(startLatch, finishLatch);
>> >> +
>> >> +        InitializerTestThread execThread1 = new
>> >> InitializerTestThread(initializer);
>> >> +        InitializerTestThread execThread2 = new
>> >> InitializerTestThread(initializer);
>> >> +
>> >> +        // Start the first thread and wait for it to get into the
>> >> initialize method so we are sure it is the thread
>> >> +        //  executing initialize().
>> >> +        execThread1.start();
>> >> +        startLatch.await();
>> >> +
>> >> +        // Start the second thread and interrupt it to force the
>> >> InterruptedException.  There is no need to make sure
>> >> +        //  the thread reaches the await() call before interrupting
>> it.
>> >> +        execThread2.start();
>> >> +
>> >> +        if ( interruptInd ) {
>> >> +            // Interrupt the second thread now and wait for it to
>> >> complete to ensure it reaches the wait inside the
>> >> +            //  get() method.
>> >> +            execThread2.interrupt();
>> >> +            execThread2.join();
>> >> +        }
>> >> +
>> >> +        // Signal the completion of the initialize method now.
>> >> +        finishLatch.countDown();
>> >> +
>> >> +        // Wait for the initialize() to finish.
>> >> +        execThread1.join();
>> >> +
>> >> +        // Wait for thread2 to finish, if it was not already done
>> >> +        if ( ! interruptInd ) {
>> >> +            execThread2.join();
>> >> +        }
>> >> +
>> >> +        //
>> >> +        // Validate: thread1 should have the valid result; thread2
>> should
>> >> have caught an interrupted exception, if
>> >> +        //  interrupted, or should have the same result otherwise.
>> >> +        //
>> >> +        assertFalse(execThread1.isCaughtException());
>> >> +        assertSame(initializer.getAnswer(), execThread1.getResult());
>> >> +
>> >> +        if ( interruptInd ) {
>> >> +            assertTrue(execThread2.isCaughtException());
>> >> +            Exception exc = (Exception) execThread2.getResult();
>> >> +            assertTrue(exc.getCause() instanceof
>> InterruptedException);
>> >> +            assertEquals("interrupted waiting for initialization to
>> >> complete", exc.getMessage());
>> >> +        } else {
>> >> +            assertFalse(execThread2.isCaughtException());
>> >> +            assertSame(initializer.getAnswer(),
>> execThread2.getResult());
>> >> +        }
>> >> +    }
>> >> +
>> >>      /**
>> >>       * A concrete test implementation of {@code
>> AtomicSafeInitializer}.
>> >> This
>> >>       * implementation also counts the number of invocations of the
>> >> initialize()
>> >> @@ -78,4 +186,90 @@ public class AtomicSafeInitializerTest e
>> >>              return new Object();
>> >>          }
>> >>      }
>> >> +
>> >> +    /**
>> >> +     * A concrete test implementation of {@code
>> AtomicSafeInitializer}.
>> >> This
>> >> +     * implementation always throws an exception.
>> >> +     */
>> >> +    private class ExceptionThrowingAtomicSafeInitializerTestImpl
>> extends
>> >> AtomicSafeInitializer<Object> {
>> >> +        @Override
>> >> +        protected Object initialize() throws ConcurrentException {
>> >> +            throw new ConcurrentException(testExceptionMessage,
>> >> testCauseException);
>> >> +        }
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * Initializer that signals it has started and waits to complete
>> >> until signalled in order to enable a guaranteed
>> >> +     * order-of-operations.  This allows the test code to peg one
>> thread
>> >> to the initialize method for a period of time
>> >> +     * that the test can dictate.
>> >> +     */
>> >> +    private class WaitingInitializerTestImpl extends
>> >> AtomicSafeInitializer<Object> {
>> >> +        private final CountDownLatch startedLatch;
>> >> +        private final CountDownLatch finishLatch;
>> >> +        private final Object answer = new Object();
>> >> +
>> >> +        public WaitingInitializerTestImpl(CountDownLatch startedLatch,
>> >> CountDownLatch finishLatch) {
>> >> +            this.startedLatch = startedLatch;
>> >> +            this.finishLatch = finishLatch;
>> >> +        }
>> >> +
>> >> +        @Override
>> >> +        protected Object initialize() throws ConcurrentException {
>> >> +            this.startedLatch.countDown();
>> >> +            try {
>> >> +                this.finishLatch.await();
>> >> +            } catch (InterruptedException intExc) {
>> >> +                throw new ConcurrentException(intExc);
>> >> +            }
>> >> +
>> >> +            return  answer;
>> >> +        }
>> >> +
>> >> +        public Object getAnswer () {
>> >> +            return answer;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    /**
>> >> +     * Test executor of the initializer get() operation that captures
>> the
>> >> result.
>> >> +     */
>> >> +    private class InitializerTestThread extends Thread {
>> >> +        private AtomicSafeInitializer<Object>   initializer;
>> >> +        private Object result;
>> >> +        private boolean caughtException;
>> >> +
>> >> +        public InitializerTestThread(AtomicSafeInitializer<Object>
>> >> initializer) {
>> >> +            super("AtomicSafeInitializer test thread");
>> >> +            this.initializer = initializer;
>> >> +        }
>> >> +
>> >> +        @Override
>> >> +        public void run() {
>> >> +            try {
>> >> +                this.result = initializer.get();
>> >> +            } catch ( ConcurrentException concurrentExc ) {
>> >> +                this.caughtException = true;
>> >> +                this.result = concurrentExc;
>> >> +            }
>> >> +        }
>> >> +
>> >> +        /**
>> >> +         * Resulting object, if the get() method returned
>> successfully,
>> >> or exception if an exception was thrown.
>> >> +         *
>> >> +         * @return resulting object or exception from the get() method
>> >> call.
>> >> +         */
>> >> +        public Object getResult () {
>> >> +            return  this.result;
>> >> +        }
>> >> +
>> >> +        /**
>> >> +         * Determine whether an exception was caught on the get()
>> call.
>> >> Does not guarantee that the get() method was
>> >> +         * called or completed.
>> >> +         *
>> >> +         * @return true => exception was caught; false => exception
>> was
>> >> not caught.
>> >> +         */
>> >> +        public boolean  isCaughtException () {
>> >> +            return  this.caughtException;
>> >> +        }
>> >> +    }
>> >>  }
>> >>
>> >> Modified:
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
>> >> URL:
>> >>
>> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
>> >>
>> >>
>> ==============================================================================
>> >> ---
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
>> >> (original)
>> >> +++
>> >>
>> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
>> >> Mon Feb 23 20:15:49 2015
>> >> @@ -17,6 +17,7 @@
>> >>  package org.apache.commons.lang3.concurrent;
>> >>
>> >>  import org.junit.Before;
>> >> +import org.junit.Test;
>> >>
>> >>  /**
>> >>   * Test class for {@code LazyInitializer}.
>> >> @@ -26,10 +27,16 @@ import org.junit.Before;
>> >>  public class LazyInitializerTest extends
>> >> AbstractConcurrentInitializerTest {
>> >>      /** The initializer to be tested. */
>> >>      private LazyInitializerTestImpl initializer;
>> >> +    private ExceptionThrowingLazyInitializerTestImpl
>> >> exceptionThrowingInitializer;
>> >> +    private Exception testCauseException;
>> >> +    private String testExceptionMessage;
>> >>
>> >>      @Before
>> >>      public void setUp() throws Exception {
>> >>          initializer = new LazyInitializerTestImpl();
>> >> +        exceptionThrowingInitializer = new
>> >> ExceptionThrowingLazyInitializerTestImpl();
>> >> +        testExceptionMessage = "x-test-exception-message-x";
>> >> +        testCauseException = new Exception(testExceptionMessage);
>> >>      }
>> >>
>> >>      /**
>> >> @@ -43,6 +50,18 @@ public class LazyInitializerTest extends
>> >>          return initializer;
>> >>      }
>> >>
>> >> +    @Override
>> >> +    protected ConcurrentInitializer<Object>
>> >> createExceptionThrowingInitializer() {
>> >> +        return exceptionThrowingInitializer;
>> >> +    }
>> >> +
>> >> +    @Test
>> >> +    public void testGetConcurrentWithException ()
>> >> +            throws ConcurrentException, InterruptedException {
>> >> +
>> >> +        super.testGetConcurrentWithException(testExceptionMessage,
>> >> testCauseException);
>> >> +    }
>> >> +
>> >>      /**
>> >>       * A test implementation of LazyInitializer. This class creates a
>> >> plain
>> >>       * Object. As Object does not provide a specific equals() method,
>> it
>> >> is easy
>> >> @@ -55,4 +74,16 @@ public class LazyInitializerTest extends
>> >>              return new Object();
>> >>          }
>> >>      }
>> >> +
>> >> +
>> >> +    /**
>> >> +     * A concrete test implementation of {@code
>> AtomicSafeInitializer}.
>> >> This
>> >> +     * implementation always throws an exception.
>> >> +     */
>> >> +    private class ExceptionThrowingLazyInitializerTestImpl extends
>> >> LazyInitializer<Object> {
>> >> +        @Override
>> >> +        protected Object initialize() throws ConcurrentException {
>> >> +            throw new ConcurrentException(testExceptionMessage,
>> >> testCauseException);
>> >> +        }
>> >> +    }
>> >>  }
>> >>
>> >>
>> >>
>> >
>> >
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
>> For additional commands, e-mail: dev-h...@commons.apache.org
>>
>>
>
>
> --
> http://people.apache.org/~britter/
> http://www.systemoutprintln.de/
> http://twitter.com/BenediktRitter
> http://github.com/britter
>



-- 
http://people.apache.org/~britter/
http://www.systemoutprintln.de/
http://twitter.com/BenediktRitter
http://github.com/britter

Reply via email to