commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r1071401 - in /commons/proper/lang/trunk/src: main/java/org/apache/commons/lang3/concurrent/ConcurrentUtils.java test/java/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java
Date Wed, 16 Feb 2011 21:20:31 GMT
Author: oheger
Date: Wed Feb 16 21:20:27 2011
New Revision: 1071401

URL: http://svn.apache.org/viewvc?rev=1071401&view=rev
Log:
[LANG-678] Added support for ConcurrentMap.putIfAbsent() to ConcurrentUtils.

Modified:
    commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/ConcurrentUtils.java
    commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java

Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/ConcurrentUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/ConcurrentUtils.java?rev=1071401&r1=1071400&r2=1071401&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/ConcurrentUtils.java
(original)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/ConcurrentUtils.java
Wed Feb 16 21:20:27 2011
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.lang3.concurrent;
 
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -207,6 +208,105 @@ public class ConcurrentUtils {
     //-----------------------------------------------------------------------
     /**
      * <p>
+     * Puts a value in the specified {@code ConcurrentMap} if the key is not yet
+     * present. This method works similar to the {@code putIfAbsent()} method of
+     * the {@code ConcurrentMap} interface, but the value returned is different.
+     * Basically, this method is equivalent to the following code fragment:
+     *
+     * <pre>
+     * if (!map.containsKey(key)) {
+     *     map.put(key, value);
+     *     return value;
+     * } else {
+     *     return map.get(key);
+     * }
+     * </pre>
+     *
+     * except that the action is performed atomically. So this method always
+     * returns the value which is stored in the map.
+     * </p>
+     * <p>
+     * This method is <b>null</b>-safe: It accepts a <b>null</b>
map as input
+     * without throwing an exception. In this case the return value is
+     * <b>null</b>, too.
+     * </p>
+     *
+     * @param <K> the type of the keys of the map
+     * @param <V> the type of the values of the map
+     * @param map the map to be modified
+     * @param key the key of the value to be added
+     * @param value the value to be added
+     * @return the value stored in the map after this operation
+     */
+    public static <K, V> V putIfAbsent(ConcurrentMap<K, V> map, K key, V value)
{
+        if (map == null) {
+            return null;
+        }
+
+        V result = map.putIfAbsent(key, value);
+        return (result != null) ? result : value;
+    }
+
+    /**
+     * Checks if a concurrent map contains a key and creates a corresponding
+     * value if not. This method first checks the presence of the key in the
+     * given map. If it is already contained, its value is returned. Otherwise
+     * the {@code get()} method of the passed in {@link ConcurrentInitializer}
+     * is called. With the resulting object
+     * {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This
+     * handles the case that in the meantime another thread has added the key to
+     * the map. Both the map and the initializer can be <b>null</b>; in this
+     * case this method simply returns <b>null</b>.
+     *
+     * @param <K> the type of the keys of the map
+     * @param <V> the type of the values of the map
+     * @param map the map to be modified
+     * @param key the key of the value to be added
+     * @param init the {@link ConcurrentInitializer} for creating the value
+     * @return the value stored in the map after this operation; this may or may
+     * not be the object created by the {@link ConcurrentInitializer}
+     * @throws ConcurrentException if the initializer throws an exception
+     */
+    public static <K, V> V createIfAbsent(ConcurrentMap<K, V> map, K key,
+            ConcurrentInitializer<V> init) throws ConcurrentException {
+        if (map == null || init == null) {
+            return null;
+        }
+
+        V value = map.get(key);
+        if (value == null) {
+            return putIfAbsent(map, key, init.get());
+        }
+        return value;
+    }
+
+    /**
+     * Checks if a concurrent map contains a key and creates a corresponding
+     * value if not, suppressing checked exceptions. This method calls
+     * {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it
+     * is caught and re-thrown as a {@link ConcurrentRuntimeException}.
+     *
+     * @param <K> the type of the keys of the map
+     * @param <V> the type of the values of the map
+     * @param map the map to be modified
+     * @param key the key of the value to be added
+     * @param init the {@link ConcurrentInitializer} for creating the value
+     * @return the value stored in the map after this operation; this may or may
+     * not be the object created by the {@link ConcurrentInitializer}
+     * @throws ConcurrentRuntimeException if the initializer throws an exception
+     */
+    public static <K, V> V createIfAbsentUnchecked(ConcurrentMap<K, V> map,
+            K key, ConcurrentInitializer<V> init) {
+        try {
+            return createIfAbsent(map, key, init);
+        } catch (ConcurrentException cex) {
+            throw new ConcurrentRuntimeException(cex.getCause());
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>
      * Gets an implementation of <code>Future</code> that is immediately done
      * and returns the specified constant value.
      * </p>

Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java?rev=1071401&r1=1071400&r2=1071401&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java
(original)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java
Wed Feb 16 21:20:27 2011
@@ -23,6 +23,8 @@ import static org.junit.Assert.assertSam
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -405,4 +407,142 @@ public class ConcurrentUtilsTest {
         assertFalse(test.cancel(false));
     }
 
+    //-----------------------------------------------------------------------
+    /**
+     * Tests putIfAbsent() if the map contains the key in question.
+     */
+    @Test
+    public void testPutIfAbsentKeyPresent() {
+        final String key = "testKey";
+        final Integer value = 42;
+        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
+        map.put(key, value);
+        assertEquals("Wrong result", value,
+                ConcurrentUtils.putIfAbsent(map, key, 0));
+        assertEquals("Wrong value in map", value, map.get(key));
+    }
+
+    /**
+     * Tests putIfAbsent() if the map does not contain the key in question.
+     */
+    @Test
+    public void testPutIfAbsentKeyNotPresent() {
+        final String key = "testKey";
+        final Integer value = 42;
+        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
+        assertEquals("Wrong result", value,
+                ConcurrentUtils.putIfAbsent(map, key, value));
+        assertEquals("Wrong value in map", value, map.get(key));
+    }
+
+    /**
+     * Tests putIfAbsent() if a null map is passed in.
+     */
+    @Test
+    public void testPutIfAbsentNullMap() {
+        assertNull("Wrong result",
+                ConcurrentUtils.putIfAbsent(null, "test", 100));
+    }
+
+    /**
+     * Tests createIfAbsent() if the key is found in the map.
+     */
+    @Test
+    public void testCreateIfAbsentKeyPresent() throws ConcurrentException {
+        @SuppressWarnings("unchecked")
+        ConcurrentInitializer<Integer> init = EasyMock
+                .createMock(ConcurrentInitializer.class);
+        EasyMock.replay(init);
+        final String key = "testKey";
+        final Integer value = 42;
+        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
+        map.put(key, value);
+        assertEquals("Wrong result", value,
+                ConcurrentUtils.createIfAbsent(map, key, init));
+        assertEquals("Wrong value in map", value, map.get(key));
+        EasyMock.verify(init);
+    }
+
+    /**
+     * Tests createIfAbsent() if the map does not contain the key in question.
+     */
+    @Test
+    public void testCreateIfAbsentKeyNotPresent() throws ConcurrentException {
+        @SuppressWarnings("unchecked")
+        ConcurrentInitializer<Integer> init = EasyMock
+                .createMock(ConcurrentInitializer.class);
+        final String key = "testKey";
+        final Integer value = 42;
+        EasyMock.expect(init.get()).andReturn(value);
+        EasyMock.replay(init);
+        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
+        assertEquals("Wrong result", value,
+                ConcurrentUtils.createIfAbsent(map, key, init));
+        assertEquals("Wrong value in map", value, map.get(key));
+        EasyMock.verify(init);
+    }
+
+    /**
+     * Tests createIfAbsent() if a null map is passed in.
+     */
+    @Test
+    public void testCreateIfAbsentNullMap() throws ConcurrentException {
+        @SuppressWarnings("unchecked")
+        ConcurrentInitializer<Integer> init = EasyMock
+                .createMock(ConcurrentInitializer.class);
+        EasyMock.replay(init);
+        assertNull("Wrong result",
+                ConcurrentUtils.createIfAbsent(null, "test", init));
+        EasyMock.verify(init);
+    }
+
+    /**
+     * Tests createIfAbsent() if a null initializer is passed in.
+     */
+    @Test
+    public void testCreateIfAbsentNullInit() throws ConcurrentException {
+        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
+        final String key = "testKey";
+        final Integer value = 42;
+        map.put(key, value);
+        assertNull("Wrong result",
+                ConcurrentUtils.createIfAbsent(map, key, null));
+        assertEquals("Map was changed", value, map.get(key));
+    }
+
+    /**
+     * Tests createIfAbsentUnchecked() if no exception is thrown.
+     */
+    @Test
+    public void testCreateIfAbsentUncheckedSuccess() {
+        final String key = "testKey";
+        final Integer value = 42;
+        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
+        assertEquals("Wrong result", value,
+                ConcurrentUtils.createIfAbsentUnchecked(map, key,
+                        new ConstantInitializer<Integer>(value)));
+        assertEquals("Wrong value in map", value, map.get(key));
+    }
+
+    /**
+     * Tests createIfAbsentUnchecked() if an exception is thrown.
+     */
+    @Test
+    public void testCreateIfAbsentUncheckedException()
+            throws ConcurrentException {
+        @SuppressWarnings("unchecked")
+        ConcurrentInitializer<Integer> init = EasyMock
+                .createMock(ConcurrentInitializer.class);
+        Exception ex = new Exception();
+        EasyMock.expect(init.get()).andThrow(new ConcurrentException(ex));
+        EasyMock.replay(init);
+        try {
+            ConcurrentUtils.createIfAbsentUnchecked(
+                    new ConcurrentHashMap<String, Integer>(), "test", init);
+            fail("Exception not thrown!");
+        } catch (ConcurrentRuntimeException crex) {
+            assertEquals("Wrong cause", ex, crex.getCause());
+        }
+        EasyMock.verify(init);
+    }
 }



Mime
View raw message