Changing the test to run with both version, the bad Cache as well as CacheOK. Making sure the same test passes for CacheOK
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/samples/deadlock/src/org/apidesign/javamonitorflaws/CacheOK.java Thu Feb 12 10:55:02 2009 +0100
1.3 @@ -0,0 +1,54 @@
1.4 +package org.apidesign.javamonitorflaws;
1.5 +
1.6 +import java.util.HashMap;
1.7 +import java.util.Map;
1.8 +
1.9 +/** Classical caching support class that makes sure there is
1.10 + * always one "To" value for each "From" one returned from the {@link #get}
1.11 + * method. However it does not prevent multiple threads to call
1.12 + * {@link #createItem} multiple times for the same "From" value.
1.13 + * <p>
1.14 + * In contrast to {@link Cache}, this is correctly synchronized.
1.15 + *
1.16 + * @author Jaroslav Tulach <jtulach@netbeans.org>
1.17 + */
1.18 +// BEGIN: monitor.pitfalls.CacheOK
1.19 +public abstract class CacheOK<From,To> {
1.20 + private Object LOCK = new Object();
1.21 +
1.22 + private Map<From,To> cache;
1.23 +
1.24 + protected abstract To createItem(From f);
1.25 +
1.26 + public final To get(From f) {
1.27 + To t = inspectValue(f);
1.28 + if (t != null) {
1.29 + return t;
1.30 + }
1.31 + To newT = createItem(f);
1.32 + return registerValue(f, newT);
1.33 + }
1.34 +
1.35 +
1.36 + private To inspectValue(From f) {
1.37 + synchronized (LOCK) {
1.38 + if (cache == null) {
1.39 + cache = new HashMap<From, To>();
1.40 + }
1.41 + return cache.get(f);
1.42 + }
1.43 + }
1.44 +
1.45 + private To registerValue(From f, To newT) {
1.46 + synchronized (LOCK) {
1.47 + To t = cache.get(f);
1.48 + if (t == null) {
1.49 + cache.put(f, newT);
1.50 + return newT;
1.51 + } else {
1.52 + return t;
1.53 + }
1.54 + }
1.55 + }
1.56 +}
1.57 +// END: monitor.pitfalls.CacheOK
2.1 --- a/samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java Wed Feb 11 10:16:43 2009 +0100
2.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java Thu Feb 12 10:55:02 2009 +0100
2.3 @@ -5,8 +5,6 @@
2.4 import org.netbeans.junit.NbTestCase;
2.5
2.6 public class CacheTest extends NbTestCase {
2.7 - private MultiplyCache cache = new MultiplyCache();
2.8 -
2.9 public CacheTest(String n) {
2.10 super(n);
2.11 }
2.12 @@ -48,6 +46,8 @@
2.13 public void testDeadlockWithSetter() throws Exception {
2.14 if (Boolean.getBoolean("no.failures")) return;
2.15
2.16 + final CacheToTest cache = new MultiplyCache();
2.17 +
2.18 class ToDeadlock implements Runnable, PropertyChangeListener {
2.19 int value;
2.20
2.21 @@ -98,10 +98,14 @@
2.22 org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.run:90
2.23 java.lang.Thread.run:619
2.24 */
2.25 - // BEGIN: monitor.pitfalls.block.propertychange
2.26 public void testDeadlockViaAPI() throws Exception {
2.27 if (Boolean.getBoolean("no.failures")) return;
2.28 -
2.29 + testDeadlockViaAPI(new MultiplyCache());
2.30 + }
2.31 +
2.32 + // BEGIN: monitor.pitfalls.block.propertychange
2.33 + private static void testDeadlockViaAPI(final CacheToTest cache)
2.34 + throws Exception {
2.35 class ToDeadlock implements Runnable, PropertyChangeListener {
2.36 int lastMultiply;
2.37
2.38 @@ -139,4 +143,17 @@
2.39 // END: monitor.pitfalls.brokencall
2.40 }
2.41 // END: monitor.pitfalls.block.propertychange
2.42 +
2.43 + public void testDeadlockViaAPIWithCacheOK() throws Exception {
2.44 + testDeadlockViaAPI(new MultiplyCacheOK());
2.45 + }
2.46 +
2.47 + static interface CacheToTest {
2.48 + public Integer get(String key);
2.49 +
2.50 + public void setMultiply(int m);
2.51 + public int getMultiply();
2.52 + public void addPropertyChangeListener(PropertyChangeListener l);
2.53 + public void removePropertyChangeListener(PropertyChangeListener l);
2.54 + }
2.55 }
2.56 \ No newline at end of file
3.1 --- a/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java Wed Feb 11 10:16:43 2009 +0100
3.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java Thu Feb 12 10:55:02 2009 +0100
3.3 @@ -1,6 +1,5 @@
3.4 package org.apidesign.javamonitorflaws;
3.5
3.6 -import org.apidesign.javamonitorflaws.Cache;
3.7 import java.beans.PropertyChangeListener;
3.8 import java.beans.PropertyChangeSupport;
3.9
3.10 @@ -9,7 +8,8 @@
3.11 * @author Jaroslav Tulach <jtulach@netbeans.org>
3.12 */
3.13 // BEGIN: monitor.pitfalls.subclass
3.14 -public class MultiplyCache extends Cache<String,Integer> {
3.15 +public class MultiplyCache extends Cache<String,Integer>
3.16 +implements CacheTest.CacheToTest {
3.17 private PropertyChangeSupport pcs;
3.18 private int multiply;
3.19 public static final String PROP_MULTIPLY = "multiply";
3.20 @@ -41,7 +41,7 @@
3.21
3.22 @Override
3.23 protected Integer createItem(String f) {
3.24 - return f.length() * getMultiply();
3.25 + return f.length() * multiply;
3.26 }
3.27 }
3.28 // END: monitor.pitfalls.subclass
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCacheOK.java Thu Feb 12 10:55:02 2009 +0100
4.3 @@ -0,0 +1,49 @@
4.4 +package org.apidesign.javamonitorflaws;
4.5 +
4.6 +import java.beans.PropertyChangeListener;
4.7 +import java.beans.PropertyChangeSupport;
4.8 +
4.9 +/**
4.10 + *
4.11 + * @author Jaroslav Tulach <jtulach@netbeans.org>
4.12 + */
4.13 +// BEGIN: monitor.pitfalls.subclassok
4.14 +public class MultiplyCacheOK extends CacheOK<String,Integer>
4.15 +implements CacheTest.CacheToTest {
4.16 + private PropertyChangeSupport pcs;
4.17 + private int multiply;
4.18 + public static final String PROP_MULTIPLY = "multiply";
4.19 +
4.20 + public synchronized int getMultiply() {
4.21 + return multiply;
4.22 + }
4.23 + public synchronized void setMultiply(int multiply) {
4.24 + int oldMultiply = this.multiply;
4.25 + this.multiply = multiply;
4.26 + pcs.firePropertyChange(PROP_MULTIPLY, oldMultiply, multiply);
4.27 + }
4.28 +
4.29 + public synchronized void addPropertyChangeListener(
4.30 + PropertyChangeListener listener
4.31 + ) {
4.32 + if (pcs == null) {
4.33 + pcs = new PropertyChangeSupport(this);
4.34 + }
4.35 + pcs.addPropertyChangeListener(listener);
4.36 + }
4.37 + public synchronized void removePropertyChangeListener(
4.38 + PropertyChangeListener listener
4.39 + ) {
4.40 + if (pcs != null) {
4.41 + pcs.removePropertyChangeListener(listener);
4.42 + }
4.43 + }
4.44 +
4.45 + @Override
4.46 + protected Integer createItem(String f) {
4.47 + return f.length() * multiply;
4.48 + }
4.49 +}
4.50 +// END: monitor.pitfalls.subclassok
4.51 +
4.52 +