2015-03-05 22:00 GMT+01:00 Oliver Heger <oliver.he...@oliver-heger.de>:
> > > Am 03.03.2015 um 08:35 schrieb Benedikt Ritter: > > 2015-03-02 21:34 GMT+01:00 Oliver Heger <oliver.he...@oliver-heger.de>: > > > >> > >> > >> Am 02.03.2015 um 07:23 schrieb Benedikt Ritter: > >>> 2015-03-01 22:20 GMT+01:00 Oliver Heger <oliver.he...@oliver-heger.de > >: > >>> > >>>> > >>>> > >>>> Am 01.03.2015 um 17:52 schrieb Benedikt Ritter: > >>>>> 2015-02-26 21:29 GMT+01:00 Oliver Heger < > oliver.he...@oliver-heger.de > >>> : > >>>>> > >>>>>> Hi Arthur, > >>>>>> > >>>>>> I don't have any principle objections against your implementation. > >>>>>> > >>>>>> I just don't want it as a replacement for the AtomicSafeInitializer > >>>>>> class. The latter was designed and announces itself to use only > atomic > >>>>>> variables for its synchronization and no other types of locks. This > >>>>>> premise no longer holds. If the implementation is broken and does > not > >>>>>> work as expected, we should deprecate and later remove it. > >>>>>> > >>>>> > >>>>> Just deprecating the class if it has a bug (hanging threads when an > >>>>> exception is thrown) doesn't seem right to me. > >>>>> > >>>>> How about we apply the patch and make it very clear in the release > >> notes > >>>>> and JavaDoc, that it's behavior has been changed? > >>>> > >>>> But then the name of the class is completely wrong. It is a bit like > >>>> > >>>> public class ClassWithoutSynchronization { > >>>> public void doSomething() { > >>>> *synchronized*(this) { > >>>> ... > >>>> } > >>>> } > >>>> } > >>>> > >>>> For me the name AtomicSafeInitializer indicates that only atomic > >>>> variables are used to ensure thread-safety. Everything else would be > >>>> surprising, even if it was mentioned in JavaDoc. > >>>> > >>> > >>> We have several issues here: > >>> > >>> 1. Find a name for the proposed initializer implementation. How about > >>> BlockingInitizalizer? > >>> 2. Fix the bug in AtomicSafeInitializer (hanging threads on exception). > >> My > >>> opinion is, that BusyWaitInitializer would be a much better name for > this > >>> class :o) > >>> 3. Compare the new implementation and LazyInitializer. Is the > >>> BlockingInitializer a replacement for LazyInitializer? > >> Who said that the new initializer is a replacement for LazyInitializer? > >> > > > > Nobody, I was asking a question ;-) > > Okay, so I hope I have answered it. > > Oliver > > > > > > >> > >> LazyInitializer is a direct implementation of the double-check idiom > >> for an instance field (refer to Bloch's Effective Java). It is pretty > >> lean and elegant. > >> > >> I assume that the new initializer behaves similar to this class: It uses > >> fields with volatile semantics in the first level and synchronization if > >> a conflict occurs. But it is more complex. So the question is do these > >> two classes have different properties and behavior so that it makes > >> sense to include both of them? > I currently don't really know how we can evaluate that. Do you have an idea? Sounds like we needed some kind of performance test. > >> > >> Oliver > >> > >>> > >>> Benedikt > >>> > >>> > >>>> > >>>> Oliver > >>>>> > >>>>> Benedikt > >>>>> > >>>>> > >>>>>> > >>>>>> What I would be interested in is a comparison between your > >>>>>> implementation and LazyInitializer. How do these classes behave in > >>>>>> typical scenarios and which one is the preferred option under which > >>>>>> conditions? > >>>>>> > >>>>>> If we add your proposal as an additional initializer implementation, > >> we > >>>>>> will also have to update the user's guide and give our users > >> appropriate > >>>>>> recommendations when they should use which class. > >>>>>> > >>>>>> And, we will have to pick a meaningful name for your class. > >>>>>> > >>>>>> Oliver > >>>>>> > >>>>>> Am 26.02.2015 um 20:51 schrieb Arthur Naseef: > >>>>>>> A few questions coming to mind here: > >>>>>>> > >>>>>>> - First off, how do we move this forward? > >>>>>>> - What do we do with the new implementation? > >>>>>>> - What is a good way to address the busy-wait in the original > >>>> code? > >>>>>>> - What is a good way to address the fact that all threads > will > >>>>>>> busy-wait in the original code if the initialize() method > >> throws > >>>> an > >>>>>>> exception? > >>>>>>> - What is a good way to address the original comments which > >>>>>> indicate > >>>>>>> this class will perform better when, in fact, the busy > waiting > >>>> may > >>>>>> have a > >>>>>>> significant impact on performance? > >>>>>>> - How can we address the fears? Honestly, I think the comment > is > >>>>>> less a > >>>>>>> "fear" than a belief, so how do we prove/disprove? > >>>>>>> - What is this other initializer implementation? > >>>>>>> - What are the differences in operation between the two? > >>>>>>> > >>>>>>> Oliver - I'm reading a lot of concerns here, but not seeing how you > >>>> would > >>>>>>> like to address those concerns. Please help me on that front. > >>>>>>> > >>>>>>> Note, by the way, that the new implementation operates the same as > >> the > >>>>>>> original code, to the application using it, except in the case of > an > >>>>>>> exception thrown on the initialize() call, which is problematic > now. > >>>>>> That > >>>>>>> is, this new implementation guarantees initialize() will only ever > be > >>>>>>> called one time, and it ensures all callers receive the result of > >> that > >>>>>>> initialize() method. > >>>>>>> > >>>>>>> Art > >>>>>>> > >>>>>>> > >>>>>>> > >>>>>>> On Mon, Feb 23, 2015 at 1:47 PM, Oliver Heger < > >>>>>> oliver.he...@oliver-heger.de> > >>>>>>> wrote: > >>>>>>> > >>>>>>>> > >>>>>>>> > >>>>>>>> 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. > >>>>>>>> > >>>>>>>> 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 > >>>>>>>> > >>>>>>>> > >>>>>>> > >>>>>> > >>>>>> > --------------------------------------------------------------------- > >>>>>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > >>>>>> For additional commands, e-mail: dev-h...@commons.apache.org > >>>>>> > >>>>>> > >>>>> > >>>>> > >>>> > >>>> --------------------------------------------------------------------- > >>>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > >>>> For additional commands, e-mail: dev-h...@commons.apache.org > >>>> > >>>> > >>> > >>> > >> > >> --------------------------------------------------------------------- > >> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > >> For additional commands, e-mail: dev-h...@commons.apache.org > >> > >> > > > > > > --------------------------------------------------------------------- > 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