You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by ba...@apache.org on 2008/10/09 20:00:33 UTC
svn commit: r703219 - in /webservices/axis2/trunk/java/modules/kernel:
src/org/apache/axis2/context/ test/org/apache/axis2/context/
Author: barrettj
Date: Thu Oct 9 11:00:33 2008
New Revision: 703219
URL: http://svn.apache.org/viewvc?rev=703219&view=rev
Log:
Replace usage of ConcurrentHashMap with a collection that can be externally locked for update to prevent ConcurrentModificationExceptions. The ConcurrentHashMap degraded performance by over 5% in some scenarios.
Modified:
webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/AbstractContext.java
webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/MessageContext.java
webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/OperationContext.java
webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceContext.java
webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceGroupContext.java
webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/SessionContext.java
webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesExternalizeTest.java
webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesTest.java
Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/AbstractContext.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/AbstractContext.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/AbstractContext.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/AbstractContext.java Thu Oct 9 11:00:33 2008
@@ -28,16 +28,11 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import java.util.AbstractSet;
-import java.util.Collection;
-import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentHashMap;
/**
* This is the top most level of the Context hierarchy and is a bag of properties.
@@ -58,7 +53,6 @@
protected long lastTouchedTime;
protected transient AbstractContext parent;
-
protected transient Map properties;
private transient Map propertyDifferences;
@@ -112,7 +106,15 @@
*/
public Iterator getPropertyNames() {
initPropertiesMap();
- return properties.keySet().iterator();
+ HashSet copyKeySet = null;
+ // Lock the table in a try/catch so it will always be unlocked in the finally block
+ try {
+ ((HashMapUpdateLockable) properties).lockForUpdate();
+ copyKeySet = new HashSet(properties.keySet());
+ } finally {
+ ((HashMapUpdateLockable) properties).unlockForUpdate();
+ }
+ return (copyKeySet != null) ? copyKeySet.iterator() : null;
}
/**
@@ -330,7 +332,7 @@
// The Map we got argument is probably NOT an instance of the Concurrent
// map we use to store properties, so create a new one using the values from the
// argument map.
- this.properties = new ConcurrentHashMapNullSemantics(properties);
+ this.properties = new HashMapUpdateLockable(properties);
}
}
}
@@ -426,262 +428,123 @@
// This needs to be a concurrent collection to prevent ConcurrentModificationExcpetions
// for async-on-the-wire. It was originally:
// properties = new HashMap(DEFAULT_MAP_SIZE);
- properties = new ConcurrentHashMapNullSemantics(DEFAULT_MAP_SIZE);
+ properties = new HashMapUpdateLockable(DEFAULT_MAP_SIZE);
}
}
}
/**
- * ConcurrentHashMap that supports the same null semantics of HashMap, which means allowing null
- * as a key and/or value. ConcurrentHashMap throws an NullPointerException if either of those
- * are null. This is done by representing null keys and/or values with a non-null value stored in
- * the collection and mapping to actual null values for the methods such as put, get, and remove.
- *
- * @param <K> Key type
- * @param <V> Value type
+ * HashMap that supports an update lock. HashMap methods that would directly update the collection
+ * will block if the collection is locked. They will be released when the collection is unlocked.
+ * Methods that do not update the collection will not be blocked.
*/
-class ConcurrentHashMapNullSemantics<K,V> extends ConcurrentHashMap<K,V> {
+class HashMapUpdateLockable extends HashMap {
- private static final long serialVersionUID = 3740068332380174316L;
-
- // Constants to represent a null key or value in the collection since actual null values
- // will cause a NullPointerExcpetion in a ConcurrentHashMap collection.
- private static final Object NULL_KEY_INDICATOR = new Object();
- private static final Object NULL_VALUE_INDICATOR = new Object();
-
- public ConcurrentHashMapNullSemantics() {
+ // Used for synchronization during update locking. Also indicates if the table is locked.
+ // Initially, it is not locked.
+ private UpdateLock updateLock = new UpdateLock(false);
+
+ HashMapUpdateLockable() {
super();
}
- public ConcurrentHashMapNullSemantics(int initialCapacity) {
- super(initialCapacity);
+ HashMapUpdateLockable(int size) {
+ super(size);
}
- public ConcurrentHashMapNullSemantics(Map<? extends K, ? extends V> map) {
+ HashMapUpdateLockable(Map map) {
super(map);
}
-
- /**
- * Similar to ConcurrentHashMap.put except null is allowed for the key and/or value.
- * @see java.util.concurrent.ConcurrentHashMap#put(java.lang.Object, java.lang.Object)
- */
- public V put(K key, V value) {
- return (V) valueFromMap(super.put((K) keyIntoMap(key), (V) valueIntoMap(value)));
- }
-
- /**
- * Similar to ConcurrentHashMap.get except null is allowed for the key and/or value.
- * @see java.util.concurrent.ConcurrentHashMap#get(java.lang.Object)
- */
- public V get(Object key) {
- return (V) valueFromMap(super.get(keyIntoMap(key)));
- }
-
+
/**
- * Similar to ConcurrentHashMap.remove except null is allowed for the key and/or value.
- * @see java.util.concurrent.ConcurrentHashMap#remove(java.lang.Object)
+ * Similar to super method but will block if collection is locked.
+ * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
*/
- public V remove(Object key) {
- // If the key is null, then look for the null key constant value in the table.
- return (V) valueFromMap(super.remove(keyIntoMap(key)));
+ public Object put(Object key, Object value) {
+ checkUpdateLock(true);
+ return super.put(key, value);
}
-
- /**
- * Similar to entrySet EXCEPT (1) nulls are allowed for keys and values and (2) this
- * does NOT RETURN a LIVE SET. Any changes made to the returned set WILL NOT be reflected in
- * the underlying collection. Also, any changes made to the collection after the set is
- * returned WILL NOT be reflected in the set. This method returns a copy of the entries in the
- * underlying collection at the point it is called.
- *
- * @see java.util.concurrent.ConcurrentHashMap#entrySet()
- */
- public Set<Map.Entry<K,V>> entrySet() {
- // The super returns a ConcurrentHashMap$EntrySet
- Set<Map.Entry<K, V>> collectionSet = super.entrySet();
- Set<Map.Entry<K, V>> returnSet = new HashSet<Map.Entry<K, V>>();
- Iterator<Map.Entry<K, V>> collectionSetIterator = collectionSet.iterator();
- // Go through the set that will be returned mapping keys and values back to null as
- // appropriate.
- while (collectionSetIterator.hasNext()) {
- Map.Entry entry = collectionSetIterator.next();
- Object useKey = keyFromMap(entry.getKey());
- Object useValue = valueFromMap(entry.getValue());
- Map.Entry<K, V> returnEntry = new SetEntry<K, V>((K) useKey, (V) useValue);
- returnSet.add(returnEntry);
- }
- return returnSet;
- }
-
+
/**
- * Similar to keySet EXCEPT (1) nulls are allowed for keys and (2) this
- * does NOT RETURN a LIVE SET. Any changes made to the returned set WILL NOT be reflected in
- * the underlying collection. Also, any changes made to the collection after the set is
- * returned WILL NOT be reflected in the set. This method returns a copy of the keys in the
- * underlying collection at the point it is called.
- *
- * @see java.util.concurrent.ConcurrentHashMap#keySet()
+ * Similar to super method but will block if collection is locked.
+ * @see java.util.HashMap#putAll(java.util.Map)
*/
- public Set<K> keySet() {
- Set<K> keySet = new SetKey<K>(super.keySet());
- return keySet;
+ public void putAll(Map map) {
+ checkUpdateLock(true);
+ super.putAll(map);
}
/**
- * Similar to containsKey except a null key is allowed.
- *
- * @see java.util.concurrent.ConcurrentHashMap#containsKey(java.lang.Object)
+ * Similar to super method but will block if collection is locked.
+ * @see java.util.HashMap#remove(java.lang.Object)
*/
- public boolean containsKey(Object key) {
- return super.containsKey(keyIntoMap(key));
+ public Object remove(Object key) {
+ checkUpdateLock(true);
+ return super.remove(key);
}
- // REVIEW: Some of these may already work. Any that are using put or get to access the elements
- // in the Map will work. These are not currently used for AbstractContext properties, so there
- // are no tests yet to verify they work (except as noted below)
- public boolean contains(Object value) {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public boolean containsValue(Object value) {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public Enumeration<V> elements() {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public Enumeration<K> keys() {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- // Note that putAll(..) works for nulls because it is using put(K,V) for each element in the
- // argument Map.
-// public void putAll(Map<? extends K, ? extends V> t) {
-// throw new UnsupportedOperationException("Not implemented for null semantics");
-// }
- public V putIfAbsent(K key, V value) {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public boolean remove(Object key, Object value) {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public boolean replace(K key, V oldValue, V newValue) {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public V replace(K key, V value) {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
- public Collection<V> values() {
- throw new UnsupportedOperationException("Not implemented for null semantics");
- }
-
/**
- * Returns a key that can be set into the collection. If the key is null, a non-null value
- * representing a null key is returned.
- * @param key The key to be set into the collection; it can be null
- * @return key if it is non-null, or a constant representing a null key if key was null.
- */
- private Object keyIntoMap(Object key) {
- if (key == null) {
- return NULL_KEY_INDICATOR;
- } else {
- return key;
+ * Lock the collection for update. NOTE: This should be called inside a try block and the
+ * unlock method should be called inside a finally block to ensure the lock is always
+ * released! If the collection is locked, methods that directly update the table will
+ * block until released by the unlock.
+ * @see #unlockForUpdate()
+ */
+ void lockForUpdate() {
+ synchronized(updateLock) {
+ updateLock.setLock(true);
}
}
/**
- * Returns a key that was retrieved from the collection. If the key the constant representing
- * a previously put null key, then null will be returned. Otherwise, the key is returned.
- * @param key The key retreived from the collection
- * @return null if the key represents a previously put key that was null, otherwise the
- * value of key is returned.
+ * Unock the collection for update. NOTE: This should be called inside a finally block and the
+ * lock method should be called inside a try block to ensure the lock is always
+ * released! If the collection is locked, methods that directly update the table will
+ * block until released by this unlock.
+ * @see #lockForUpdate()
*/
- private Object keyFromMap(Object key) {
- if (key == NULL_KEY_INDICATOR) {
- return null;
- } else {
- return key;
- }
- }
-
- /**
- * Returns a value that can be set into the collection. If the valuse is null, a non-null value
- * representing a null value is returned.
- * @param value The value to be set into the collection; it can be null
- * @return value if it is non-null, or a constant representing a null value if value was null.
- */
- private Object valueIntoMap(Object value) {
- if (value == null) {
- return NULL_VALUE_INDICATOR;
- } else {
- return value;
+ void unlockForUpdate() {
+ synchronized(updateLock) {
+ updateLock.setLock(false);
+ updateLock.notifyAll();
}
}
/**
- * Returns a value that was retrieved from the collection. If the value the constant representing
- * a previously put null value, then null will be returned. Otherwise, the value is returned.
- * @param value The value retreived from the collection
- * @return null if the value represents a previously put value that was null, otherwise the
- * value of value is returned.
+ * Check if the update lock is currently held. Optionally block until the lock is released.
+ * @param wait If true, will block until the lock is released
+ * @return true if the collection is currently locked for update, false if it is not.
*/
- private Object valueFromMap(Object value) {
- if (value == NULL_VALUE_INDICATOR) {
- return null;
- } else {
- return value;
+ boolean checkUpdateLock(boolean wait) {
+ boolean isLocked = false;
+ synchronized(updateLock) {
+ if (wait) {
+ while (updateLock.isLocked()) {
+ try {
+ updateLock.wait();
+ } catch (InterruptedException e) {
+ // Ignore the interrupt; recheck the lock and wait if appropriate.
+ }
+ }
+ }
+ isLocked = updateLock.isLocked();
}
+
+ return isLocked;
}
-
- /**
- * An Entry to be returned in a Set for the elements in the collection. Note that both the key
- * and the value may be null.
- *
- * @param <K> Key type
- * @param <V> Value type
- */
- class SetEntry<K, V> implements Map.Entry<K, V> {
- private K theKey = null;
- private V theValue = null;
-
- SetEntry(K key, V value) {
- this.theKey = key;
- this.theValue = value;
- }
-
- public K getKey() {
- return this.theKey;
- }
- public V getValue() {
- return this.theValue;
- }
+ class UpdateLock {
+ private boolean isLocked = false;
- public V setValue(V value) {
- this.theValue = value;
- return this.theValue;
+ UpdateLock(boolean isLocked) {
+ this.isLocked = isLocked;
}
-
- }
-
- /**
- * A set of Keys returned from the collection. Note that a key may be null.
- *
- * @param <K> Key type
- */
- class SetKey<K> extends AbstractSet<K> {
- Set<K> set = null;
- SetKey(Set collectionKeySet) {
- set = new HashSet<K>();
- Iterator collectionIterator = collectionKeySet.iterator();
- while (collectionIterator.hasNext()) {
- Object useKey = collectionIterator.next();
- set.add((K) keyFromMap(useKey));
- }
- }
- @Override
- public Iterator<K> iterator() {
- return set.iterator();
+
+ void setLock(boolean lockValue) {
+ this.isLocked = lockValue;
}
- @Override
- public int size() {
- return set.size();
+ boolean isLocked() {
+ return isLocked;
}
}
}
Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/MessageContext.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/MessageContext.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/MessageContext.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/MessageContext.java Thu Oct 9 11:00:33 2008
@@ -3295,7 +3295,7 @@
log.trace(getLogIDString() +
": readExternal(): About to read properties, marker is: " + marker);
}
- properties = in.readMap(new ConcurrentHashMapNullSemantics());
+ properties = in.readMap(new HashMapUpdateLockable());
//---------------------------------------------------------
Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/OperationContext.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/OperationContext.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/OperationContext.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/OperationContext.java Thu Oct 9 11:00:33 2008
@@ -572,7 +572,7 @@
// properties
//---------------------------------------------------------
in.readUTF(); // read marker
- properties = in.readMap(new ConcurrentHashMapNullSemantics());
+ properties = in.readMap(new HashMapUpdateLockable());
//---------------------------------------------------------
// axis operation meta data
Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceContext.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceContext.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceContext.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceContext.java Thu Oct 9 11:00:33 2008
@@ -434,7 +434,7 @@
//---------------------------------------------------------
// properties
//---------------------------------------------------------
- properties = in.readMap(new ConcurrentHashMapNullSemantics());
+ properties = in.readMap(new HashMapUpdateLockable());
//---------------------------------------------------------
// AxisService
Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceGroupContext.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceGroupContext.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceGroupContext.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/ServiceGroupContext.java Thu Oct 9 11:00:33 2008
@@ -378,7 +378,7 @@
//---------------------------------------------------------
// properties
//---------------------------------------------------------
- properties = in.readMap(new ConcurrentHashMapNullSemantics());
+ properties = in.readMap(new HashMapUpdateLockable());
//---------------------------------------------------------
// AxisServiceGroup
Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/SessionContext.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/SessionContext.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/SessionContext.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/SessionContext.java Thu Oct 9 11:00:33 2008
@@ -275,7 +275,7 @@
//---------------------------------------------------------
// properties
//---------------------------------------------------------
- properties = in.readMap(new ConcurrentHashMapNullSemantics());
+ properties = in.readMap(new HashMapUpdateLockable());
//---------------------------------------------------------
// "nested"
Modified: webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesExternalizeTest.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesExternalizeTest.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesExternalizeTest.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesExternalizeTest.java Thu Oct 9 11:00:33 2008
@@ -41,7 +41,7 @@
mc.setProperty("key4_nullValue", null);
mc.setProperty("key5", "value5");
- assertTrue(mc.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(mc.properties instanceof HashMapUpdateLockable);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -63,7 +63,7 @@
assertEquals("value3_nullKey", mcRead.getProperty(null));
assertNull(mcRead.getProperty("key4_nullValue"));
assertEquals("value5", mcRead.getProperty("key5"));
- assertTrue(mcRead.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(mcRead.properties instanceof HashMapUpdateLockable);
} catch (Exception ex) {
ex.printStackTrace();
@@ -82,7 +82,7 @@
ctx.setProperty("key4_nullValue", null);
ctx.setProperty("key5", "value5");
- assertTrue(ctx.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctx.properties instanceof HashMapUpdateLockable);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -104,7 +104,7 @@
assertEquals("value3_nullKey", ctxRead.getProperty(null));
assertNull(ctxRead.getProperty("key4_nullValue"));
assertEquals("value5", ctxRead.getProperty("key5"));
- assertTrue(ctxRead.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctxRead.properties instanceof HashMapUpdateLockable);
} catch (Exception ex) {
ex.printStackTrace();
@@ -121,7 +121,7 @@
ctx.setProperty("key4_nullValue", null);
ctx.setProperty("key5", "value5");
- assertTrue(ctx.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctx.properties instanceof HashMapUpdateLockable);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -143,7 +143,7 @@
assertEquals("value3_nullKey", ctxRead.getProperty(null));
assertNull(ctxRead.getProperty("key4_nullValue"));
assertEquals("value5", ctxRead.getProperty("key5"));
- assertTrue(ctxRead.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctxRead.properties instanceof HashMapUpdateLockable);
} catch (Exception ex) {
ex.printStackTrace();
@@ -160,7 +160,7 @@
ctx.setProperty("key4_nullValue", null);
ctx.setProperty("key5", "value5");
- assertTrue(ctx.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctx.properties instanceof HashMapUpdateLockable);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -182,7 +182,7 @@
assertEquals("value3_nullKey", ctxRead.getProperty(null));
assertNull(ctxRead.getProperty("key4_nullValue"));
assertEquals("value5", ctxRead.getProperty("key5"));
- assertTrue(ctxRead.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctxRead.properties instanceof HashMapUpdateLockable);
} catch (Exception ex) {
ex.printStackTrace();
@@ -199,7 +199,7 @@
ctx.setProperty("key4_nullValue", null);
ctx.setProperty("key5", "value5");
- assertTrue(ctx.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctx.properties instanceof HashMapUpdateLockable);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -221,7 +221,7 @@
assertEquals("value3_nullKey", ctxRead.getProperty(null));
assertNull(ctxRead.getProperty("key4_nullValue"));
assertEquals("value5", ctxRead.getProperty("key5"));
- assertTrue(ctxRead.properties instanceof ConcurrentHashMapNullSemantics);
+ assertTrue(ctxRead.properties instanceof HashMapUpdateLockable);
} catch (Exception ex) {
ex.printStackTrace();
Modified: webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesTest.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesTest.java?rev=703219&r1=703218&r2=703219&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesTest.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/test/org/apache/axis2/context/ContextPropertiesTest.java Thu Oct 9 11:00:33 2008
@@ -21,289 +21,423 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
import junit.framework.TestCase;
/**
- * Test the setting of properties on an AbstractContext instance. Originally the properties
- * collection was a HashMap, but was changed to a Concurrent collection because multiple threads
- * could be accessing the properties at the same time for aysnc wire flows, when processing
- * responses.
- *
- * The intent is to retain the original semantics of the HashMap which allows null keys and null
- * values, since areas of the code seem to make use of those semantics. Note that most of the
- * tests below use MessageContext as the concreate implemntation of the abstract
- * AbstractContext.
+ * Test the setting of properties on an AbstractContext instance. Note that MessageContext is used
+ * in most of the tests as the concrete implementation of AbstractContext. Some tests also test
+ * the HashTableUpdateLockable collection used in AbstractContext.proeprties directly.
*/
public class ContextPropertiesTest extends TestCase {
/**
- * Test basic property setting, including using null keys and values.
+ * Test to ensure using an Iterator from getPropertyNames() after the properties have been
+ * updated does not cause a ConcurrentModificationException. The CME does not happen because
+ * the Iterator is over a copy of the key names, not the actual collection (which would cause
+ * a CME in this test).
*/
- public void testProperties() {
+ public void testPropertyNamesForCME() {
MessageContext mc = new MessageContext();
- // Test getting a non-existent key returns a null value
- assertNull(mc.getProperty("NonexistenKey"));
-
- // Test setting a null property value;
- mc.setProperty("testProperty1", null);
- assertNull(mc.getProperty("testProperty1"));
-
- // Test setting a null property key
- mc.setProperty(null, "value");
- Object value = mc.getProperty(null);
- assertEquals("value", (String) value);
-
- // Test setting a null key and value
- mc.setProperty(null, null);
- assertNull(mc.getProperty(null));
-
- // Test setting a null value after a valid value; the next get should return null
- String property2 = "testProperty2";
- String testValue2 = "value2";
- mc.setProperty(property2, testValue2);
- Object value2 = mc.getProperty(property2);
- assertEquals(testValue2, value2);
- mc.setProperty(property2, null);
- assertNull(mc.getProperty(property2));
-
+ mc.setProperty("key1", "value1");
+ mc.setProperty("key2", "value2");
+ mc.setProperty(null, "value3_nullkey");
+ mc.setProperty("key4_nullvalue", null);
+
+ // Get an iterator over the key names, then add a new element.
+ // This simulates two threads accessing the context. This should not cause
+ // a ConcurrentModificationException (it will if a live iterator over the collection
+ // key set is returned by getPropertyNames())
+ Iterator propNamesIterator = mc.getPropertyNames();
+ mc.setProperty("newKey", "newValue");
+ while (propNamesIterator.hasNext()) {
+ // Verify
+ // (1) that calling next() on the iterator does not cause a CME,
+ // (2) that the keys are as expected and
+ // (3) those keys can be used to access the entries in the collection.
+ String checkKey = (String) propNamesIterator.next();
+ Iterator concurrentIterator = mc.getPropertyNames();
+ while (concurrentIterator.hasNext()) {
+ String concurrentKey = (String) concurrentIterator.next();
+ }
+ }
}
/**
- * Test setting the properties based on an input Map. NOTE that MessageContext has logic
- * in setProperties(Map) that always performs a copy by setting a COPY_PROPERTIES flag.
- * There there is an additional test below that uses a mock object implemntation of
- * AbstractContext to make sure the collection remains a concurrent one.
- * @see #testPropertiesAssignment_MockAbstractContext()
+ * Test that using an iterator of propertyNames does not cause a CME. This is degenerate
+ * test of the test for CME that updates the collection before using the iterator.
+ * @see #testPropertyNamesForCME()
*/
- public void testPropertiesAssignment() {
+ public void testPropertyNamesForCME_NoUpdate() {
MessageContext mc = new MessageContext();
- Map map = new HashMap();
- map.put("keyFromMap1", "valueFromMap1");
- map.put("keyFromMap2", "valueFromMap2");
- map.put("keyFromMap3", null);
- map.put(null, "valueFromMapNullKey");
-
-
- mc.setProperties(map);
- assertEquals("valueFromMap1", mc.getProperty("keyFromMap1"));
- assertEquals("valueFromMap2", mc.getProperty("keyFromMap2"));
- assertEquals(null, mc.getProperty("keyFromMap3"));
- assertEquals("valueFromMapNullKey", mc.getProperty(null));
- }
+ mc.setProperty("key1", "value1");
+ mc.setProperty("key2", "value2");
+ mc.setProperty(null, "value3_nullkey");
+ mc.setProperty("key4_nullvalue", null);
+ Iterator propNamesIterator = mc.getPropertyNames();
+ while (propNamesIterator.hasNext()) {
+ Iterator concurrentIterator = mc.getPropertyNames();
+ // Verify
+ // (1) that calling next() on the iterator does not cause a CME,
+ // (2) that the keys are as expected and
+ // (3) those keys can be used to access the entries in the collection.
+ String checkKey = (String) propNamesIterator.next();
+ }
+ }
+
/**
- * Test setting the properties based on an input Map. Make sure that the resulting collection
- * on the AbstractContext is a Concurrent collection.
- * @see #testPropertiesAssignment()
+ * Test some aspects of the Concurrent collection directly, such as creating with a Map.
*/
- public void testPropertiesAssignment_MockAbstractContext() {
- MockAbstractContext mc = new MockAbstractContext();
- assertTrue(mc.getProperties() instanceof ConcurrentHashMapNullSemantics);
+ public void testHashMapUpdateLockable() {
+
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ testMap.put("key", "value");
+ testMap.put(1, 2);
Map map = new HashMap();
- map.put("keyFromMap1", "valueFromMap1");
- map.put("keyFromMap2", "valueFromMap2");
- map.put("keyFromMap3", null);
- map.put(null, "valueFromMapNullKey");
+ map.put("k1", "v1");
+ map.put("k2", "v2");
+ map.put(null, "v3NullKey");
+ map.put("k3_null", null);
+ HashMapUpdateLockable testCtorMap = new HashMapUpdateLockable(map);
+ assertEquals("v1", testCtorMap.get("k1"));
+ assertEquals("v2", testCtorMap.get("k2"));
+ assertEquals("v3NullKey", testCtorMap.get(null));
+ assertNull(testCtorMap.get("k3_null"));
- mc.setProperties(map);
- assertEquals("valueFromMap1", mc.getProperty("keyFromMap1"));
- assertEquals("valueFromMap2", mc.getProperty("keyFromMap2"));
- assertEquals(null, mc.getProperty("keyFromMap3"));
- assertEquals("valueFromMapNullKey", mc.getProperty(null));
- assertTrue(mc.getProperties() instanceof ConcurrentHashMapNullSemantics);
+ // put returns the previous value if there was one, or null
+ assertEquals("v1", testCtorMap.put("k1", "newK1Value"));
+ assertNull(testCtorMap.put("k3_null", "newK3Value"));
+ assertNull(testCtorMap.put("noSuchKey-put", "value6"));
+ // remove returns the value if there was an entry with that key, or null
+ assertNull(testCtorMap.remove("noSuchKey-remove"));
+ testCtorMap.put("key7", null);
+ assertNull(testCtorMap.remove("key7"));
+
}
+ // =============================================================================================
+ // The following tests are multithreaded tests to test the locking, blocking, and releasing
+ // of threads by the HashTableUpdateLockable collection.
+ // =============================================================================================
+
/**
- * Test removing elements from the collection.
+ * Methods on HashTableUpdateLockable to be tested. The update methods are tested to
+ * verify they block while the updateLock is held. Some non-update methods are tested to
+ * verify they do not block if the updateLock is held.
*/
- public void testPropertiesRemove() {
- MessageContext mc = new MessageContext();
- // Remove null key not previously added, make sure it is gone
- mc.removeProperty(null);
-
- // Remove null key previously added
- mc.setProperty(null, "ValueForNullKey");
- assertEquals("ValueForNullKey", mc.getProperty(null));
- mc.removeProperty(null);
- assertNull(mc.getProperty(null));
-
- // Remove non-existent key
- assertNull(mc.getProperty("NonexistentKey"));
- mc.removeProperty("NonexistentKey");
- assertNull(mc.getProperty("NonexistentKey"));
-
- // Remove non-null key, non-null value make sure it is gone
- mc.setProperty("nonNullKey1", "nonNullValue1");
- assertEquals("nonNullValue1", mc.getProperty("nonNullKey1"));
- mc.removeProperty("nonNullKey1");
- assertNull(mc.getProperty("nonNullKey1"));
-
- // Remove non-null key, null value & make sure it is gone
- mc.setProperty("nonNullKey2", null);
- assertEquals(null, mc.getProperty("nonNullKey2"));
- mc.removeProperty("nonNullKey2");
- assertNull(mc.getProperty("nonNullKey2"));
-
- }
+ private enum MethodToCheck {checkLock_wait, checkLock_nowait, put, putAll, remove, get};
- public void testCollectionEntrySet() {
- MockAbstractContext mc = new MockAbstractContext();
- mc.setProperty("key1", "value1");
- mc.setProperty("key2", "value2");
- mc.setProperty("key3", null);
- mc.setProperty(null, "value4-nullkey");
- mc.setProperty("key5", "value5");
-
- Map checkProperties = mc.getProperties();
- Set entrySet = checkProperties.entrySet();
- Iterator entryIterator = entrySet.iterator();
- int correctEntries = 0;
- while (entryIterator.hasNext()) {
- Map.Entry entry = (Map.Entry) entryIterator.next();
- // The collection uses Object instances to represent nulls in the collection. If
- // the conversion back to nulls hasn't occured, that will cause a CastClassException
- // when it is casted to a (String).
- try {
- String checkKey = (String) entry.getKey();
- String checkValue = (String) entry.getValue();
- if ("key1".equals(checkKey) && "value1".equals(checkValue)) {
- correctEntries++;
- } else if ("key2".equals(checkKey) && "value2".equals(checkValue)) {
- correctEntries++;
- } else if ("key3".equals(checkKey) && (checkValue == null)) {
- correctEntries++;
- } else if ((checkKey == null) && "value4-nullkey".equals(checkValue)) {
- correctEntries++;
- } else if ("key5".equals(checkKey) && "value5".equals(checkValue)) {
- correctEntries++;
- } else {
- fail("Invalid entry: key: " + checkKey + ", value: " + checkValue);
- }
- } catch (ClassCastException e) {
- e.printStackTrace();
- fail("Caught ClassCastException " + e.toString());
- }
- }
- assertEquals(5, correctEntries);
+ /**
+ * Test HashTableUpdateLockable.remove method which overrides the HashTable method.
+ * Tests the HashTableUpdateLockable collection in a multithreaded environment to ensure that
+ * a thread trying to update the collection will block as long as another thread has the
+ * collection locked for update. Note that UpdateRunnable performs the actual test of the
+ * method.
+ * @see UpdateRunnable#run()
+ *
+ */
+ public void testMultithreadUpdateLock_remove() {
+ MultithreadUpdateLockMonitor testMonitor = new MultithreadUpdateLockMonitor();
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ testMap.put("testKey1", "value1");
+ testMap.put("removeKey", "value2");
+ startupTestThreads(testMonitor, testMap, MethodToCheck.remove);
+
+ assertNoThreadErrors(testMonitor);
+
+ // Make sure the update thread blocked, i.e. the lockThread was released before the update thread.
+ assertTrue(testMonitor.lockThreadReleaseTime <= testMonitor.updateThreadReleaseTime);
}
- public void testCollectionKeySet() {
- MockAbstractContext mc = new MockAbstractContext();
- mc.setProperty("key1", "value1");
- mc.setProperty("key2", "value2");
- mc.setProperty("key3", null);
- mc.setProperty(null, "value4-nullkey");
- mc.setProperty("key5", "value5");
-
- Map checkProperties = mc.getProperties();
- Set keySet = checkProperties.keySet();
- Iterator keyIterator = keySet.iterator();
- int correctEntries = 0;
- while (keyIterator.hasNext()) {
- // The collection uses Object instances to represent nulls in the collection. If
- // the conversion back to nulls hasn't occured, that will cause a CastClassException
- // when it is casted to a (String).
- try {
- String checkKey = (String) keyIterator.next();
- if ("key1".equals(checkKey)) {
- correctEntries++;
- } else if ("key2".equals(checkKey)) {
- correctEntries++;
- } else if ("key3".equals(checkKey)) {
- correctEntries++;
- } else if ((checkKey == null)) {
- correctEntries++;
- } else if ("key5".equals(checkKey)) {
- correctEntries++;
- } else {
- fail("Invalid entry: key: " + checkKey);
- }
- } catch (ClassCastException e) {
- fail("Caught ClassCastException " + e.toString());
- }
- }
- assertEquals(5, correctEntries);
+ /**
+ * Test HashTableUpdateLockable.putAll method which overrides the HashTable method.
+ * Tests the HashTableUpdateLockable collection in a multithreaded environment to ensure that
+ * a thread trying to update the collection will block as long as another thread has the
+ * collection locked for update. Note that UpdateRunnable performs the actual test of the
+ * method.
+ * @see UpdateRunnable#run()
+ */
+ public void testMultithreadUpdateLock_putAll() {
+ MultithreadUpdateLockMonitor testMonitor = new MultithreadUpdateLockMonitor();
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ startupTestThreads(testMonitor, testMap, MethodToCheck.putAll);
+
+ assertNoThreadErrors(testMonitor);
+
+ // Make sure the update thread blocked, i.e. the lockThread was released before the update thread.
+ assertTrue(testMonitor.lockThreadReleaseTime <= testMonitor.updateThreadReleaseTime);
}
-
- public void testCollectionContainsKey() {
- MockAbstractContext mc = new MockAbstractContext();
- mc.setProperty("key1", "value1");
- mc.setProperty("key2", "value2");
- mc.setProperty("key3", null);
- mc.setProperty(null, "value4-nullkey");
- mc.setProperty("key5", "value5");
-
- Map checkCollection = mc.getProperties();
- assertTrue(checkCollection.containsKey("key1"));
- assertTrue(checkCollection.containsKey(null));
- assertFalse(checkCollection.containsKey("notHere"));
+ /**
+ * Test HashTableUpdateLockable.put method which overrides the HashTable method.
+ * Tests the HashTableUpdateLockable collection in a multithreaded environment to ensure that
+ * a thread trying to update the collection will block as long as another thread has the
+ * collection locked for update. Note that UpdateRunnable performs the actual test of the
+ * method.
+ * @see UpdateRunnable#run()
+ */
+ public void testMultithreadUpdateLock_put() {
+ MultithreadUpdateLockMonitor testMonitor = new MultithreadUpdateLockMonitor();
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ startupTestThreads(testMonitor, testMap, MethodToCheck.put);
+
+ assertNoThreadErrors(testMonitor);
+
+ // Make sure the update thread blocked, i.e. the lockThread was released before the update thread.
+ assertTrue(testMonitor.lockThreadReleaseTime <= testMonitor.updateThreadReleaseTime);
}
/**
- * Test some aspects of the Concurrent collection directly, such as creating with and without
- * generics, and creating with a Map.
+ * Test HashTableUpdateLockable.put method which overrides the HashTable method.
+ * Tests the HashTableUpdateLockable collection in a multithreaded environment to ensure that
+ * a thread trying to update the collection does not block as long as another thread has the
+ * collection locked for update. Note that UpdateRunnable performs the actual test of the
+ * method.
+ * @see UpdateRunnable#run()
*/
- public void testConcurrentHashMapNullSemantics() {
-
- ConcurrentHashMapNullSemantics noGenerics = new ConcurrentHashMapNullSemantics();
- noGenerics.put("key", "value");
- noGenerics.put(1, 2);
-
- ConcurrentHashMapNullSemantics<String, String> stringGenerics = new ConcurrentHashMapNullSemantics<String, String>();
- stringGenerics.put("key", "value");
-
- ConcurrentHashMapNullSemantics<Integer, Integer> integerGenerics = new ConcurrentHashMapNullSemantics<Integer, Integer>();
- integerGenerics.put(new Integer(1), new Integer(2));
-
- Map<String, String> map = new HashMap<String, String>();
- map.put("k1", "v1");
- map.put("k2", "v2");
- map.put(null, "v3NullKey");
- map.put("k3_null", null);
- ConcurrentHashMapNullSemantics<String, String> useMapGenerics = new ConcurrentHashMapNullSemantics<String, String>(map);
- assertEquals("v1", useMapGenerics.get("k1"));
- assertEquals("v2", useMapGenerics.get("k2"));
- assertEquals("v3NullKey", useMapGenerics.get(null));
- assertNull(useMapGenerics.get("k3_null"));
-
- // put returns the previous value if there was one, or null
- assertEquals("v1", useMapGenerics.put("k1", "newK1Value"));
- assertNull(useMapGenerics.put("k3_null", "newK3Value"));
- assertNull(useMapGenerics.put("noSuchKey-put", "value6"));
+ public void testMultithreadUpdateLock_get() {
+ MultithreadUpdateLockMonitor testMonitor = new MultithreadUpdateLockMonitor();
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ testMap.put("key1", "value1");
+ testMap.put("key2", "value2");
+ testMap.put("getKey", "value3");
+ startupTestThreads(testMonitor, testMap, MethodToCheck.get);
+
+ assertNoThreadErrors(testMonitor);
+
+ // The update thread shouldn't block since we said to not wait.
+ assertTrue(testMonitor.updateThreadReleaseTime <= testMonitor.lockThreadReleaseTime);
+ }
+
+ /**
+ * Test HashTableUpdateLockable.checkUpdateLock method.
+ * Tests the HashTableUpdateLockable collection in a multithreaded environment to ensure that
+ * a thread trying to update the collection will block as long as another thread has the
+ * collection locked for update. Note that UpdateRunnable performs the actual test of the
+ * method.
+ * @see UpdateRunnable#run()
+ */
+ public void testMultithreadUpdateLock_checkLock_wait() {
+ MultithreadUpdateLockMonitor testMonitor = new MultithreadUpdateLockMonitor();
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ startupTestThreads(testMonitor, testMap, MethodToCheck.checkLock_wait);
+
+ assertNoThreadErrors(testMonitor);
+
+ // Make sure the update thread blocked, i.e. the lockThread was released before the update thread.
+ assertTrue(testMonitor.lockThreadReleaseTime <= testMonitor.updateThreadReleaseTime);
+ // Make sure the return value indicates the table was not locked.
+ assertFalse(((Boolean) testMonitor.methodToTestReturnValue).booleanValue());
+ }
+
+ /**
+ * Test HashTableUpdateLockable.checkUpdateLock method.
+ * Tests the HashTableUpdateLockable collection in a multithreaded environment to ensure that
+ * a thread trying to update the collection will not block as long as another thread has the
+ * collection locked for update. Note that UpdateRunnable performs the actual test of the
+ * method.
+ * @see UpdateRunnable#run()
+ */
+ public void testMultithreadUpdateLock_checkLock_nowait() {
+ MultithreadUpdateLockMonitor testMonitor = new MultithreadUpdateLockMonitor();
+ HashMapUpdateLockable testMap = new HashMapUpdateLockable();
+ startupTestThreads(testMonitor, testMap, MethodToCheck.checkLock_nowait);
+
+ assertNoThreadErrors(testMonitor);
+
+ // The update thread shouldn't block since we said to not wait.
+ assertTrue(testMonitor.updateThreadReleaseTime <= testMonitor.lockThreadReleaseTime);
+ // Make sure the return value indicates the table was locked.
+ assertTrue(((Boolean) testMonitor.methodToTestReturnValue).booleanValue());
+ }
+
+ // Amount of time the testcase should wait on the test threads before timing out
+ private static int THREAD_TIMEOUT = 90000;
+ // Amount of time the thread holding the update lock should sleep. This needs to be long
+ // enough so that comparisons between the time the locking thread is released and the update
+ // thread is released can be reliably compared as an indication of whether the update thread
+ // was blocked.
+ private static int LOCK_THREAD_SLEEP = 5000;
+
+ /**
+ * Assert there were no errors in the basic running of the threads.
+ * @param testMonitor contains information about each of the test threads.
+ */
+ private void assertNoThreadErrors(MultithreadUpdateLockMonitor testMonitor) {
+ // Make sure both threads were released (i.e. release time != 0) and that the
+ // that there were no exceptions encountered
- // remove returns the value if there was an entry with that key, or null
- assertNull(useMapGenerics.remove("noSuchKey-remove"));
- useMapGenerics.put("key7", null);
- assertNull(useMapGenerics.remove("key7"));
+ assertNull(testMonitor.lockThreadException);
+ assertTrue(testMonitor.lockThreadReleaseTime != 0);
- ConcurrentHashMapNullSemantics useMapNoGenerics = new ConcurrentHashMapNullSemantics(map);
- assertEquals("v1", useMapNoGenerics.get("k1"));
- assertEquals("v2", useMapNoGenerics.get("k2"));
-
+ assertNull(testMonitor.updateThreadException);
+ assertTrue(testMonitor.updateThreadReleaseTime != 0);
}
-}
+ /**
+ * Configures the test threads with the common object to store information about each one, the
+ * collection to be tested and the method to be tested. Two threads will be started:
+ * (1) A thread that will lock the collection, sleep, then unlock the collection
+ * (2) A thread that will try to update the collection while it is locked.
+ * Thread (2) should block if the method to be tested is one that would update the table,
+ * otherwise it should not block.
+ *
+ * @param testMonitor Common object used to communicate between the test method and the two
+ * test threads
+ * @param testMap Instance of HashTableUpdateLockable to test
+ * @param methodToTest The method on HashTableUpdateLockable to be tested.
+ */
+ private void startupTestThreads(MultithreadUpdateLockMonitor testMonitor,
+ HashMapUpdateLockable testMap,
+ MethodToCheck methodToTest) {
+ LockingRunnable lockingRunnable = new LockingRunnable();
+ lockingRunnable.testMonitor = testMonitor;
+ lockingRunnable.testMap = testMap;
+ lockingRunnable.methodToTest = methodToTest;
+
+ UpdateRunnable updateRunnable = new UpdateRunnable();
+ updateRunnable.testMonitor = testMonitor;
+ updateRunnable.testMap = testMap;
+ updateRunnable.methodToTest = methodToTest;
+
+ Thread lockingThread = new Thread(lockingRunnable, "Locking");
+ Thread updateThread = new Thread(updateRunnable, "Updating");
+
+ // To eliminate a timing window where the locking thread starts and runs to completion
+ // before the update thread gets started, start the update thread first. It will block
+ // waiting on the locking thread to complete.
+ updateThread.start();
+ lockingThread.start();
+
+ // Join the threads to wait for their completion, specifying a timeout to prevent
+ // a testcase hang if something goes wrong with the threads.
+ try {
+ lockingThread.join(THREAD_TIMEOUT);
+ updateThread.join(THREAD_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ fail("Unable to join to testing threads");
+ }
+ }
+
+ /**
+ * Object used to communicate information between the testcase and the two test threads.
+ */
+ class MultithreadUpdateLockMonitor {
+ long lockThreadReleaseTime = 0;
+ boolean lockSetupComplete = false;
+ RuntimeException lockThreadException = null;
-class MockAbstractContext extends AbstractContext {
+ long updateThreadReleaseTime = 0;
+ RuntimeException updateThreadException = null;
- MockAbstractContext() {
- properties = new ConcurrentHashMapNullSemantics();
+ Object methodToTestReturnValue = null;
}
- @Override
- public ConfigurationContext getRootContext() {
- return null;
+ /**
+ * Abstract superclass of the two test threads.
+ */
+ abstract class UpdateLockableTestRunnable implements Runnable {
+ MultithreadUpdateLockMonitor testMonitor = null;
+ MethodToCheck methodToTest = null;
+ HashMapUpdateLockable testMap = null;
}
- public Map getProperties() {
- return properties;
+
+ /**
+ * Test thread that will lock the collection, sleep, then unlock the collection.
+ */
+ class LockingRunnable extends UpdateLockableTestRunnable {
+
+ public void run() {
+ try {
+ // Lock the table, then relese the "lockSetupComplete" waiters, which will
+ // release the update thread.
+ // NOTE that the lock is done inside a try block and the unlock is done in
+ // a finaly block so that it will always be executed.
+ testMap.lockForUpdate();
+ synchronized (testMonitor) {
+ testMonitor.lockThreadReleaseTime = 0;
+ testMonitor.lockSetupComplete = true;
+ testMonitor.notifyAll();
+ }
+ // Sleep for a while to verify that the update thread is being blocked while
+ // the table is locked by this thread. Then unlock the table.
+ try {
+ Thread.sleep(LOCK_THREAD_SLEEP);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ testMonitor.lockThreadException = new RuntimeException(e);
+ throw testMonitor.lockThreadException;
+ }
+ } finally {
+ // Get the current time before releasing the lock. On some systems, as soon as the
+ // notify is done, the waiting thread will immediately get control.
+ testMonitor.lockThreadReleaseTime = System.currentTimeMillis();
+ testMap.unlockForUpdate();
+ }
+ }
}
+ /**
+ * Thread that will run the methods to be tested on the collection while it is locked for
+ * update by another thread.
+ */
+ class UpdateRunnable extends UpdateLockableTestRunnable {
+ public void run() {
+ // Setup whatever we'll need to test laser on
+ Map putAllMap = new HashMap();
+ putAllMap.put("k1", "v1");
+ putAllMap.put("k2", "v2");
+ putAllMap.put("k3", "v3");
+
+ // Wait till the locking thread is setup
+ synchronized(testMonitor) {
+ testMonitor.updateThreadReleaseTime = 0;
+ while(!testMonitor.lockSetupComplete) {
+ try {
+ testMonitor.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ testMonitor.updateThreadException = new RuntimeException(e);
+ throw testMonitor.updateThreadException;
+ }
+ }
+ }
+
+ // Test the update method specified
+ switch(methodToTest) {
+ case checkLock_wait:
+ boolean retValWait = testMap.checkUpdateLock(true);
+ testMonitor.methodToTestReturnValue = new Boolean(retValWait);
+ break;
+ case checkLock_nowait:
+ boolean retValNoWait = testMap.checkUpdateLock(false);
+ testMonitor.methodToTestReturnValue = new Boolean(retValNoWait);
+ break;
+ case put:
+ testMap.put("newKey", "newValue");
+ break;
+ case putAll:
+ testMap.putAll(putAllMap);
+ break;
+ case remove:
+ testMap.remove("removeKey");
+ break;
+ case get:
+ testMap.get("getKey");
+ break;
+ default:
+ testMonitor.updateThreadException =
+ new UnsupportedOperationException("method to test not recognized: " + methodToTest);
+ throw testMonitor.updateThreadException;
+ }
+ testMonitor.updateThreadReleaseTime = System.currentTimeMillis();
+ }
+ }
}