1515 */
1616package org .owasp .esapi .reference ;
1717
18- import org .owasp .esapi .AccessReferenceMap ;
19- import org .owasp .esapi .errors .AccessControlException ;
20-
18+ import java .util .HashMap ;
19+ import java .util .HashSet ;
2120import java .util .Iterator ;
2221import java .util .Map ;
2322import java .util .Set ;
2423import java .util .TreeSet ;
25- import java .util .concurrent .ConcurrentHashMap ;
24+
25+ import org .owasp .esapi .AccessReferenceMap ;
26+ import org .owasp .esapi .errors .AccessControlException ;
2627
2728/**
28- * Abstract Implementation of the AccessReferenceMap that is backed by ConcurrentHashMaps to
29- * provide a thread-safe implementation of the AccessReferenceMap. Implementations of this
30- * abstract class should implement the #getUniqueReference() method.
31- *
29+ * Abstract Implementation of the AccessReferenceMap.
30+ * <br>
31+ * Implementation offers default synchronization on all public API
32+ * to assist with thread safety.
33+ * <br>
34+ * For complex interactions spanning multiple calls, it is recommended
35+ * to add a synchronized block around all invocations to maintain intended data integrity.
36+ *
37+ * <pre>
38+ * public MyClassUsingAARM {
39+ * private AbstractAccessReferenceMap<Object> aarm;
40+ *
41+ * public void replaceAARMDirect(Object oldDirect, Object newDirect) {
42+ * synchronized (aarm) {
43+ * aarm.removeDirectReference(oldDirect);
44+ * aarm.addDirectReference(newDirect);
45+ * }
46+ * }
47+ * }
48+ * </pre>
3249 * @author Chris Schmidt ([email protected] ) 3350 * @since July 21, 2009
3451 */
@@ -43,15 +60,15 @@ public abstract class AbstractAccessReferenceMap<K> implements AccessReferenceMa
4360
4461 /**
4562 * Instantiates a new access reference map. Note that this will create the underlying Maps with an initialSize
46- * of {@link ConcurrentHashMap #DEFAULT_INITIAL_CAPACITY} and that resizing a Map is an expensive process. Consider
63+ * of {@link HashMap #DEFAULT_INITIAL_CAPACITY} and that resizing a Map is an expensive process. Consider
4764 * using a constructor where the initialSize is passed in to maximize performance of the AccessReferenceMap.
4865 *
4966 * @see #AbstractAccessReferenceMap(java.util.Set, int)
5067 * @see #AbstractAccessReferenceMap(int)
5168 */
5269 public AbstractAccessReferenceMap () {
53- itod = new ConcurrentHashMap <K , Object >();
54- dtoi = new ConcurrentHashMap <Object ,K >();
70+ itod = new HashMap <K , Object >();
71+ dtoi = new HashMap <Object ,K >();
5572 }
5673
5774 /**
@@ -62,8 +79,8 @@ public AbstractAccessReferenceMap() {
6279 * The initial size of the underlying maps
6380 */
6481 public AbstractAccessReferenceMap ( int initialSize ) {
65- itod = new ConcurrentHashMap <K , Object >(initialSize );
66- dtoi = new ConcurrentHashMap <Object ,K >(initialSize );
82+ itod = new HashMap <K , Object >(initialSize );
83+ dtoi = new HashMap <Object ,K >(initialSize );
6784 }
6885
6986 /**
@@ -82,8 +99,8 @@ public AbstractAccessReferenceMap( int initialSize ) {
8299 */
83100 @ Deprecated
84101 public AbstractAccessReferenceMap ( Set <Object > directReferences ) {
85- itod = new ConcurrentHashMap <K , Object >(directReferences .size ());
86- dtoi = new ConcurrentHashMap <Object ,K >(directReferences .size ());
102+ itod = new HashMap <K , Object >(directReferences .size ());
103+ dtoi = new HashMap <Object ,K >(directReferences .size ());
87104 update (directReferences );
88105 }
89106
@@ -111,8 +128,8 @@ public AbstractAccessReferenceMap( Set<Object> directReferences ) {
111128 */
112129 @ Deprecated
113130 public AbstractAccessReferenceMap ( Set <Object > directReferences , int initialSize ) {
114- itod = new ConcurrentHashMap <K , Object >(initialSize );
115- dtoi = new ConcurrentHashMap <Object ,K >(initialSize );
131+ itod = new HashMap <K , Object >(initialSize );
132+ dtoi = new HashMap <Object ,K >(initialSize );
116133 update (directReferences );
117134 }
118135
@@ -135,7 +152,7 @@ public synchronized Iterator iterator() {
135152 /**
136153 * {@inheritDoc}
137154 */
138- public <T > K addDirectReference (T direct ) {
155+ public synchronized <T > K addDirectReference (T direct ) {
139156 if ( dtoi .keySet ().contains ( direct ) ) {
140157 return dtoi .get ( direct );
141158 }
@@ -148,7 +165,7 @@ public <T> K addDirectReference(T direct) {
148165 /**
149166 * {@inheritDoc}
150167 */
151- public <T > K removeDirectReference (T direct ) throws AccessControlException
168+ public synchronized <T > K removeDirectReference (T direct ) throws AccessControlException
152169 {
153170 K indirect = dtoi .get (direct );
154171 if ( indirect != null ) {
@@ -162,33 +179,52 @@ public <T> K removeDirectReference(T direct) throws AccessControlException
162179 * {@inheritDoc}
163180 */
164181 public final synchronized void update (Set directReferences ) {
165- Map <Object ,K > new_dtoi = new ConcurrentHashMap <Object ,K >( directReferences .size () );
166- Map <K ,Object > new_itod = new ConcurrentHashMap <K ,Object >( directReferences .size () );
167-
168- for ( Object o : directReferences ) {
169- K indirect = dtoi .get ( o );
170-
171- if ( indirect == null ) {
172- indirect = getUniqueReference ();
173- }
174- new_dtoi .put ( o , indirect );
175- new_itod .put ( indirect , o );
176- }
177- dtoi = new_dtoi ;
178- itod = new_itod ;
182+ Map <Object ,K > new_dtoi = new HashMap <Object ,K >( directReferences .size () );
183+ Map <K ,Object > new_itod = new HashMap <K ,Object >( directReferences .size () );
184+
185+ Set <Object > newDirect = new HashSet <>(directReferences );
186+ Set <Object > dtoiCurrent = new HashSet <>(dtoi .keySet ());
187+
188+ //Preserve all keys that are in the new set
189+ dtoiCurrent .retainAll (newDirect );
190+
191+ //Transfer existing values into the new map
192+ for (Object current : dtoiCurrent ) {
193+ K idCurrent = dtoi .get (current );
194+ new_dtoi .put (current , idCurrent );
195+ new_itod .put (idCurrent , current );
196+ }
197+
198+ //Trim the new map to only new values
199+ newDirect .removeAll (dtoiCurrent );
200+
201+ //Add new values with new indirect keys to the new map
202+ for (Object newD : newDirect ) {
203+ K idCurrent ;
204+ do {
205+ idCurrent = getUniqueReference ();
206+ //Unlikey, but just in case we generate the exact same key multiple times...
207+ } while (dtoi .containsValue (idCurrent ));
208+
209+ new_dtoi .put (newD , idCurrent );
210+ new_itod .put (idCurrent , newD );
211+ }
212+
213+ dtoi = new_dtoi ;
214+ itod = new_itod ;
179215 }
180216
181217 /**
182218 * {@inheritDoc}
183219 */
184- public <T > K getIndirectReference (T directReference ) {
220+ public synchronized <T > K getIndirectReference (T directReference ) {
185221 return dtoi .get (directReference );
186222 }
187223
188224 /**
189225 * {@inheritDoc}
190226 */
191- public <T > T getDirectReference (K indirectReference ) throws AccessControlException {
227+ public synchronized <T > T getDirectReference (K indirectReference ) throws AccessControlException {
192228 if (itod .containsKey (indirectReference ) ) {
193229 try
194230 {
0 commit comments