From 6d8a46e82540d035d4ba53a7760cc7edca2391af Mon Sep 17 00:00:00 2001 From: "Richard G. Curtis" Date: Thu, 20 Feb 2014 16:33:30 +0000 Subject: [PATCH] OPENJPA-2441: Remove usage of NullSafeConcurrentHashMap. Patch contributed by Dalia Abo Sheasha. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1570261 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/datacache/AbstractQueryCache.java | 28 ++- .../openjpa/kernel/QueryStatistics.java | 26 ++- .../apache/openjpa/util/ProxyManagerImpl.java | 37 ++-- .../apache/openjpa/util/TestProxyManager.java | 34 ++++ .../concurrent/NullSafeConcurrentHashMap.java | 11 +- .../concurrent/SizedConcurrentHashMap.java | 6 +- .../util/concurrent/TestConcurrentMap.java | 149 --------------- .../TestNullSafeConcurrentHashMap.java | 179 ------------------ 8 files changed, 108 insertions(+), 362 deletions(-) delete mode 100644 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java delete mode 100644 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestNullSafeConcurrentHashMap.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java index 09d91e37c..56324d27f 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java @@ -40,9 +40,10 @@ import org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; +import org.apache.openjpa.lib.util.ReferenceMap; import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager; +import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet; -import org.apache.openjpa.lib.util.concurrent.SizedConcurrentHashMap; import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.MetaDataRepository; import org.apache.openjpa.util.Id; @@ -480,11 +481,27 @@ public abstract class AbstractQueryCache private long[] astat = new long[ARRAY_SIZE]; private long[] stat = new long[ARRAY_SIZE]; - private Map stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); - private Map astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); + private Map stats; + private Map astats; private Date start = new Date(); private Date since = start; - + + public Default() { + initializeMaps(); + } + + private void initializeMaps() { + ConcurrentReferenceHashMap statsMap = + new ConcurrentReferenceHashMap(ReferenceMap.HARD, ReferenceMap.HARD, CONCURRENCY, LOAD_FACTOR); + statsMap.setMaxSize(FIXED_SIZE); + stats = statsMap; + + ConcurrentReferenceHashMap aStatsMap = + new ConcurrentReferenceHashMap(ReferenceMap.HARD, ReferenceMap.HARD, CONCURRENCY, LOAD_FACTOR); + aStatsMap.setMaxSize(FIXED_SIZE); + astats = aStatsMap; + } + public Set keys() { return stats.keySet(); } @@ -552,8 +569,7 @@ public abstract class AbstractQueryCache public synchronized void clear() { astat = new long[ARRAY_SIZE]; stat = new long[ARRAY_SIZE]; - stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); - astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); + initializeMaps(); start = new Date(); since = start; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java index 0bc18dd9b..40eec08aa 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java @@ -25,7 +25,8 @@ import java.util.Date; import java.util.Map; import java.util.Set; -import org.apache.openjpa.lib.util.concurrent.SizedConcurrentHashMap; +import org.apache.openjpa.lib.util.ReferenceMap; +import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; /** * Records query execution statistics. @@ -153,11 +154,27 @@ public interface QueryStatistics extends Serializable { private long[] astat = new long[ARRAY_SIZE]; private long[] stat = new long[ARRAY_SIZE]; - private Map stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); - private Map astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); + private Map stats; + private Map astats; private Date start = new Date(); private Date since = start; + public Default() { + initializeMaps(); + } + + private void initializeMaps() { + ConcurrentReferenceHashMap statsMap = + new ConcurrentReferenceHashMap(ReferenceMap.HARD, ReferenceMap.HARD, CONCURRENCY, LOAD_FACTOR); + statsMap.setMaxSize(FIXED_SIZE); + stats = statsMap; + + ConcurrentReferenceHashMap aStatsMap = + new ConcurrentReferenceHashMap(ReferenceMap.HARD, ReferenceMap.HARD, CONCURRENCY, LOAD_FACTOR); + aStatsMap.setMaxSize(FIXED_SIZE); + astats = aStatsMap; + } + public Set keys() { return stats.keySet(); } @@ -216,8 +233,7 @@ public interface QueryStatistics extends Serializable { public synchronized void clear() { astat = new long[ARRAY_SIZE]; stat = new long[ARRAY_SIZE]; - stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); - astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY); + initializeMaps(); start = new Date(); since = start; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java index 0c2e5da2c..e71c91dba 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java @@ -56,8 +56,8 @@ import org.apache.openjpa.lib.util.Files; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Options; -import org.apache.openjpa.lib.util.concurrent.NullSafeConcurrentHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import serp.bytecode.BCClass; @@ -94,8 +94,8 @@ public class ProxyManagerImpl _stdMaps.put(SortedMap.class, TreeMap.class); } - private final Set _unproxyable = new HashSet(); - private final Map _proxies = new NullSafeConcurrentHashMap(); + private final Set _unproxyable = new HashSet(); + private final Map, Proxy> _proxies = new ConcurrentHashMap, Proxy>(); private boolean _trackChanges = true; private boolean _assertType = false; private boolean _delayedCollectionLoading = false; @@ -453,31 +453,32 @@ public class ProxyManagerImpl * Return the cached factory proxy for the given bean type. */ private ProxyBean getFactoryProxyBean(Object orig) { - final Class type = orig.getClass(); + final Class type = orig.getClass(); if (isUnproxyable(type)) return null; // we don't lock here; ok if two proxies get generated for same type ProxyBean proxy = (ProxyBean) _proxies.get(type); - if (proxy == null && !_proxies.containsKey(type)) { - ClassLoader l = GeneratedClasses.getMostDerivedLoader(type, - ProxyBean.class); - Class pcls = loadBuildTimeProxy(type, l); + if (proxy == null) { + ClassLoader l = GeneratedClasses.getMostDerivedLoader(type, ProxyBean.class); + Class pcls = loadBuildTimeProxy(type, l); if (pcls == null) { - // TODO Move this to J2DOPrivHelper? - BCClass bc = AccessController - .doPrivileged(new PrivilegedAction() { - public BCClass run() { - return generateProxyBeanBytecode(type, true); - } - }); + // TODO Move this to J2DOPrivHelper? + BCClass bc = AccessController.doPrivileged(new PrivilegedAction() { + public BCClass run() { + return generateProxyBeanBytecode(type, true); + } + }); if (bc != null) pcls = GeneratedClasses.loadBCClass(bc, l); } if (pcls != null) - proxy = (ProxyBean) instantiateProxy(pcls, - findCopyConstructor(type), new Object[] {orig}); - _proxies.put(type, proxy); + proxy = (ProxyBean) instantiateProxy(pcls, findCopyConstructor(type), new Object[] { orig }); + if (proxy == null) { + _unproxyable.add(type.getName()); + } else { + _proxies.put(type, proxy); + } } return proxy; } diff --git a/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java b/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java index 1e4794444..7d27eada0 100644 --- a/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java +++ b/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java @@ -740,8 +740,35 @@ public class TestProxyManager extends TestCase { NonproxyableBean orig = new NonproxyableBean(1); populate(orig); assertNull(_mgr.copyCustom(orig)); + assertNull(_mgr.copyCustom(orig)); assertNull(_mgr.newCustomProxy(orig, true)); } + + public void testIsUnproxyable() { + CustomBean validBean = new CustomBean(); + populate(validBean); + assertNotNull(_mgr.copyCustom(validBean)); + assertNotNull(_mgr.newCustomProxy(validBean, true)); + assertFalse(_mgr.isUnproxyable(CustomBean.class)); + + NonproxyableBean bean1 = new NonproxyableBean(1); + populate(bean1); + + NonproxyableBean2 bean2 = new NonproxyableBean2(); + populate(bean2); + + assertFalse(_mgr.isUnproxyable(NonproxyableBean.class)); + assertNull(_mgr.copyCustom(bean1)); + assertTrue(_mgr.isUnproxyable(NonproxyableBean.class)); + assertNull(_mgr.newCustomProxy(bean1, true)); + assertTrue(_mgr.isUnproxyable(NonproxyableBean.class)); + + assertFalse(_mgr.isUnproxyable(NonproxyableBean2.class)); + assertNull(_mgr.newCustomProxy(bean2, true)); + assertTrue(_mgr.isUnproxyable(NonproxyableBean2.class)); + assertNull(_mgr.copyCustom(bean2)); + assertTrue(_mgr.isUnproxyable(NonproxyableBean2.class)); + } /** * Assert that the given beans are exactly the same. @@ -948,6 +975,13 @@ public class TestProxyManager extends TestCase { } } + /** + * Used to non-proxyable custom bean handling. + */ + public class NonproxyableBean2 extends CustomBean { + // class is not static + } + /** * Used to test custom calendar handling. */ diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/NullSafeConcurrentHashMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/NullSafeConcurrentHashMap.java index 6d86bf949..611b15fb2 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/NullSafeConcurrentHashMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/NullSafeConcurrentHashMap.java @@ -31,12 +31,15 @@ import java.util.HashSet; import org.apache.commons.collections.set.MapBackedSet; /** - * A subclass of {@link ConcurrentHashMap} that allows null keys and values. - * In exchange, it weakens the contract of {@link #putIfAbsent} and the other - * concurrent methods added in {@link #ConcurrentHashMap}. - * + * A subclass of {@link ConcurrentHashMap} that allows null keys and values. In exchange, it weakens the contract of + * {@link #putIfAbsent} and the other concurrent methods added in {@link #ConcurrentHashMap}. + * * @since 1.1.0 + * @deprecated In Java 8, java.util.ConcurrentHashMap received an overhauled and this extension was not updated. This + * class will fail to compile on Java 8. If it is compiled at a lower level and run on Java 8 it will not + * work properly. For more information: https://issues.apache.org/jira/browse/OPENJPA-2441 */ +@Deprecated public class NullSafeConcurrentHashMap extends ConcurrentHashMap { private enum Markers { diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/SizedConcurrentHashMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/SizedConcurrentHashMap.java index b030a1f9d..f3e141f5c 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/SizedConcurrentHashMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/SizedConcurrentHashMap.java @@ -28,9 +28,13 @@ import org.apache.openjpa.lib.util.SizedMap; /** * An implementation of {@link SizedMap} that uses JDK1.5 concurrency primitives - * + * * @since 1.1.0 + * @deprecated In Java 8, java.util.ConcurrentHashMap received an overhauled and this extension was not updated. This + * class will fail to compile on Java 8. If it is compiled at a lower level and run on Java 8 it will not + * work properly. For more information: https://issues.apache.org/jira/browse/OPENJPA-2441 */ +@Deprecated public class SizedConcurrentHashMap extends NullSafeConcurrentHashMap implements SizedMap, ConcurrentMap, Serializable { diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java deleted file mode 100644 index 58e8100ea..000000000 --- a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.lib.util.concurrent; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import org.apache.openjpa.lib.util.ReferenceMap; -import org.apache.openjpa.lib.test.AbstractTestCase; - -/** - * Tests the methods of {@link ConcurrentMap}. - * - * @author Abe White - */ -public class TestConcurrentMap extends AbstractTestCase { - - private static final int ENTRIES = 333; - private static final int SLEEP = 3; - - private ConcurrentMap[] _maps = new ConcurrentMap[]{ - new SizedConcurrentHashMap(ENTRIES, .75f, 16), - new ConcurrentReferenceHashMap(ReferenceMap.HARD, ReferenceMap.HARD), }; - - public void setUp() throws Exception { - super.setUp(); - for (int i = 0; i < ENTRIES; i++) { - for (int j = 0; j < _maps.length; j++) { - int key = j * ENTRIES + i; - _maps[j].put(new Integer(key), "v" + key); - } - } - for (int i = 0; i < _maps.length; i++) - assertEquals(ENTRIES, _maps[i].size()); - } - - public void testRemoveRandom() { - Set keys = new TreeSet(); - for (int i = 0; i < ENTRIES; i++) - for (int j = 0; j < _maps.length; j++) - assertTrue(removeRandom(_maps[j], keys)); - postRemoveTest(keys); - } - - private static boolean removeRandom(ConcurrentMap map, Set keys) { - Map.Entry rem = map.removeRandom(); - return rem != null && rem.getValue().equals("v" + rem.getKey()) - && keys.add(rem.getKey()); - } - - private void postRemoveTest(Set keys) { - for (int i = 0; i < _maps.length; i++) { - assertTrue(_maps[i].isEmpty()); - assertTrue(!_maps[i].containsKey(new Integer(ENTRIES * i + i))); - } - assertEquals(keys.toString(), ENTRIES * _maps.length, keys.size()); - } - - public synchronized void testRemoveRandomThreaded() - throws InterruptedException { - Set keys = Collections.synchronizedSet(new TreeSet()); - RemoveRandomRunnable[] runs = - new RemoveRandomRunnable[ENTRIES * _maps.length]; - for (int i = 0; i < ENTRIES; i++) - for (int j = 0; j < _maps.length; j++) - runs[j * ENTRIES + i] = new RemoveRandomRunnable - (_maps[j], keys); - for (int i = 0; i < runs.length; i++) - new Thread(runs[i]).start(); - Thread.currentThread().sleep(SLEEP * ENTRIES * _maps.length); - for (int i = 0; i < runs.length; i++) { - assertTrue(String.valueOf(i), !runs[i].error); - if (runs[i].interrupted) - throw new InterruptedException(String.valueOf(i)); - } - postRemoveTest(keys); - } - - public void testIterate() { - iterationTest(false); - } - - private List iterationTest(boolean random) { - Set keys = new TreeSet(); - List ordered = new ArrayList(200); - for (int i = 0; i < _maps.length; i++) { - Iterator itr = (random) ? _maps[i].randomEntryIterator() - : _maps[i].entrySet().iterator(); - while (itr.hasNext()) { - Map.Entry entry = (Map.Entry) itr.next(); - assertEquals("v" + entry.getKey(), entry.getValue()); - assertTrue(keys + ":: " + _maps[i].getClass() + "::" - + entry.getKey() + "::" + entry.getValue(), - keys.add(entry.getKey())); - ordered.add(entry.getKey()); - } - } - assertEquals(keys.toString(), ENTRIES * _maps.length, keys.size()); - return ordered; - } - - public void testRandomIterate() { - iterationTest(true); - } - - private static class RemoveRandomRunnable implements Runnable { - - public boolean error = false; - public boolean interrupted = false; - - private final ConcurrentMap _map; - private final Set _keys; - - public RemoveRandomRunnable(ConcurrentMap map, Set keys) { - _map = map; - _keys = keys; - } - - public synchronized void run() { - try { - Thread.currentThread().sleep((long) (Math.random() * SLEEP)); - } catch (InterruptedException ie) { - interrupted = true; - } - error = !removeRandom(_map, _keys); - } - } -} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestNullSafeConcurrentHashMap.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestNullSafeConcurrentHashMap.java deleted file mode 100644 index a39620107..000000000 --- a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestNullSafeConcurrentHashMap.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.lib.util.concurrent; - -import java.io.IOException; -import java.util.Set; -import java.util.Collection; -import java.util.Map; -import java.util.HashMap; -import java.util.Map.Entry; - -import org.apache.openjpa.lib.test.AbstractTestCase; - -public class TestNullSafeConcurrentHashMap extends AbstractTestCase { - - private NullSafeConcurrentHashMap newMap() { - return new NullSafeConcurrentHashMap(); - } - - public void testRemoveRandomIsNotTotallyDeterministic() { - removeHelper(false); - } - - public void testRandomIteratorIsNotTotallyDeterministic() { - removeHelper(true); - } - - private void removeHelper(boolean iter) { - Map removedCounts = new HashMap(); - for (int i = 0; i < 1000; i++) { - NullSafeConcurrentHashMap m = new NullSafeConcurrentHashMap(); - m.put("a", "A"); - m.put("b", "B"); - m.put("c", "C"); - m.put("d", "D"); - m.put("e", "E"); - m.put("f", "F"); - m.put("g", "G"); - - String removed; - if (iter) { - removed = (String) m.removeRandom().getKey(); - } else { - removed = (String) ((Entry) m.randomEntryIterator().next()) - .getKey(); - m.remove(removed); - } - - Integer count = removedCounts.get(removed); - if (count == null) - removedCounts.put(removed, 1); - else - removedCounts.put(removed, count.intValue() + 1); - } - - // assume that over 1000 runs, every element should be removed at - // least once, and no element should be removed more than 30% of - // the time - assertEquals(7, removedCounts.size()); - for (Entry entry : removedCounts.entrySet()) { - if (entry.getValue() == 0) - fail("element " + entry.getKey() + " was never removed"); - if (entry.getValue() > 500) - fail("element " + entry.getKey() + " was removed " - + entry.getValue() + " times; this is greater than the " - + "threshold of 500."); - } - } - - public void testNullKeys() throws ClassNotFoundException, IOException { - helper(null, "value 0", "value 1", "value 2"); - } - - private void helper(Object key, Object value0, - Object value1, Object value2) - throws IOException, ClassNotFoundException { - - NullSafeConcurrentHashMap m = newMap(); - - // initial put - m.put(key, value0); - - // get etc. - assertEquals(value0, m.get(key)); - assertTrue(m.containsKey(key)); - assertTrue(m.containsValue(value0)); - - // keySet - Set keys = m.keySet(); - assertTrue(keys.contains(key)); - assertEquals(1, keys.size()); - assertEquals(key, keys.iterator().next()); - - // entrySet - Set entries = m.entrySet(); - Entry e = (Entry) entries.iterator().next(); - assertEquals(key, e.getKey()); - assertEquals(value0, e.getValue()); - - // values - Collection values = m.values(); - assertEquals(1, values.size()); - assertEquals(value0, values.iterator().next()); - - // serializability - assertEquals(m, roundtrip(m, true)); - - // put - assertEquals(value0, m.put(key, value1)); - - // remove - assertEquals(value1, m.put(key, value1)); - assertEquals(value1, m.remove(key)); - m.put(key, value1); - - // ConcurrentMap stuff - assertFalse(m.remove("invalid key", value0)); - assertTrue(m.remove(key, value1)); - assertNull(m.putIfAbsent(key, value0)); // null == prev unset - - // value0 might be null; can't disambiguate from above in OpenJPA - // interpretation - assertEquals(value0, m.putIfAbsent(key, "invalid value")); - - // replace - assertEquals(value0, m.replace(key, value1)); - assertTrue(m.replace(key, value1, value2)); - - // putAll. Note that ConcurrentHashMap happens to delegate to put() - // from within putAll() calls. This test should help ensure that we - // find out if that changes. - m = newMap(); - Map putAllArg = new HashMap(); - putAllArg.put(key, value0); - putAllArg.put("another key", value1); - m.putAll(putAllArg); - assertEquals(value0, m.get(key)); - assertEquals(value1, m.get("another key")); - } - - public void testNullValues() throws ClassNotFoundException, IOException { - nullValsHelper("foo"); - } - - private void nullValsHelper(Object key) - throws IOException, ClassNotFoundException { - helper(key, null, null, null); - helper(key, "bar", "baz", "quux"); - - helper(key, "bar", "baz", null); - helper(key, null, "baz", "quux"); - helper(key, "bar", null, "quux"); - - helper(key, "bar", null, null); - helper(key, null, "baz", null); - helper(key, null, null, "quux"); - } - - public void testNullKeysAndValues() - throws ClassNotFoundException, IOException { - nullValsHelper(null); - } -}