SLING-9631 : Avoid read/write lock when processing requests
diff --git a/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java b/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java
index 24dcf5a..e166fe6 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java
@@ -20,124 +20,94 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
public class PathBasedHolderCache<Type extends PathBasedHolder> {
- private final Map<String, Map<String, SortedSet<Type>>> cache = new HashMap<String, Map<String, SortedSet<Type>>>();
-
- /** Read/write lock to synchronize the cache access. */
- private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
+ /**
+ * The cache is a concurrent map of concurrent maps.
+ * As the final value, the sorted set is replaced on each change, reading of the
+ * cache does not need to be synchronized. Updating of the cache is synchronized.
+ */
+ private final Map<String, Map<String, SortedSet<Type>>> cache = new ConcurrentHashMap<>();
public void clear() {
- this.rwLock.writeLock().lock();
- try {
- cache.clear();
- } finally {
- this.rwLock.writeLock().unlock();
- }
+ cache.clear();
}
- public void addHolder(final Type holder) {
- this.rwLock.writeLock().lock();
- try {
+ public synchronized void addHolder(final Type holder) {
+ final Map<String, SortedSet<Type>> byHostMap = cache.computeIfAbsent(holder.protocol, protocol -> new ConcurrentHashMap<>());
- Map<String, SortedSet<Type>> byHostMap = cache.get(holder.protocol);
- if (byHostMap == null) {
- byHostMap = new HashMap<String, SortedSet<Type>>();
- cache.put(holder.protocol, byHostMap);
- }
-
- final SortedSet<Type> byPathSet = new TreeSet<Type>();
-
- // preset with current list
- final SortedSet<Type> currentPathSet = byHostMap.get(holder.host);
- if (currentPathSet != null) {
- byPathSet.addAll(currentPathSet);
- }
-
- // add the new holder
- byPathSet.add(holder);
-
- // replace old set with new set
- byHostMap.put(holder.host, byPathSet);
- } finally {
- this.rwLock.writeLock().unlock();
+ // preset with current list
+ final SortedSet<Type> currentPathSet = byHostMap.get(holder.host);
+ final SortedSet<Type> byPathSet = new TreeSet<Type>();
+ if (currentPathSet != null) {
+ byPathSet.addAll(currentPathSet);
}
+
+ // add the new holder
+ byPathSet.add(holder);
+
+ // replace old set with new set
+ byHostMap.put(holder.host, byPathSet);
}
- public void removeHolder(final Type holder) {
- this.rwLock.writeLock().lock();
- try {
- final Map<String, SortedSet<Type>> byHostMap = cache.get(holder.protocol);
- if (byHostMap != null) {
- final SortedSet<Type> byPathSet = byHostMap.get(holder.host);
- if (byPathSet != null) {
+ public synchronized void removeHolder(final Type holder) {
+ final Map<String, SortedSet<Type>> byHostMap = cache.get(holder.protocol);
+ if (byHostMap != null) {
+ final SortedSet<Type> byPathSet = byHostMap.get(holder.host);
+ if (byPathSet != null) {
- // create a new set without the removed holder
- final SortedSet<Type> set = new TreeSet<Type>();
- set.addAll(byPathSet);
- set.remove(holder);
+ // create a new set without the removed holder
+ final SortedSet<Type> set = new TreeSet<Type>();
+ set.addAll(byPathSet);
+ set.remove(holder);
- // replace the old set with the new one (or remove if empty)
- if (set.isEmpty()) {
- byHostMap.remove(holder.host);
- } else {
- byHostMap.put(holder.host, set);
- }
+ // replace the old set with the new one (or remove if empty)
+ if (set.isEmpty()) {
+ byHostMap.remove(holder.host);
+ } else {
+ byHostMap.put(holder.host, set);
}
}
- } finally {
- this.rwLock.writeLock().unlock();
}
}
public Collection<Type>[] findApplicableHolders(final HttpServletRequest request) {
- this.rwLock.readLock().lock();
- try {
- final String hostname = request.getServerName()
- + (request.getServerPort() != 80 && request.getServerPort() != 443
- ? ":" + request.getServerPort()
- : "");
+ final String hostname = request.getServerName()
+ + (request.getServerPort() != 80 && request.getServerPort() != 443
+ ? ":" + request.getServerPort()
+ : "");
- @SuppressWarnings("unchecked")
- final SortedSet<Type>[] result = new SortedSet[4];
+ @SuppressWarnings("unchecked")
+ final SortedSet<Type>[] result = new SortedSet[4];
- final Map<String, SortedSet<Type>> byHostMap = cache.get(request.getScheme());
- if ( byHostMap != null ) {
- result[0] = byHostMap.get(hostname);
- result[1] = byHostMap.get("");
- }
- final Map<String, SortedSet<Type>> defaultByHostMap = cache.get("");
- if ( defaultByHostMap != null ) {
- result[2] = defaultByHostMap.get(hostname);
- result[3] = defaultByHostMap.get("");
- }
- return result;
- } finally {
- this.rwLock.readLock().unlock();
+ final Map<String, SortedSet<Type>> byHostMap = cache.get(request.getScheme());
+ if ( byHostMap != null ) {
+ result[0] = byHostMap.get(hostname);
+ result[1] = byHostMap.get("");
}
+ final Map<String, SortedSet<Type>> defaultByHostMap = cache.get("");
+ if ( defaultByHostMap != null ) {
+ result[2] = defaultByHostMap.get(hostname);
+ result[3] = defaultByHostMap.get("");
+ }
+ return result;
}
public List<Type> getHolders() {
- this.rwLock.readLock().lock();
- try {
- final List<Type> result = new ArrayList<Type>();
- for (Map<String, SortedSet<Type>> byHostEntry : cache.values()) {
- for (SortedSet<Type> holderSet : byHostEntry.values()) {
- result.addAll(holderSet);
- }
+ final List<Type> result = new ArrayList<Type>();
+ for (Map<String, SortedSet<Type>> byHostEntry : cache.values()) {
+ for (SortedSet<Type> holderSet : byHostEntry.values()) {
+ result.addAll(holderSet);
}
- return result;
- } finally {
- this.rwLock.readLock().unlock();
}
+ return result;
}
}
diff --git a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
index 4a25f19..47f1ef9 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
@@ -25,11 +25,11 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
-import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import javax.jcr.SimpleCredentials;
import javax.security.auth.login.AccountLockedException;
@@ -1673,7 +1673,7 @@
private final PathBasedHolderCache<AbstractAuthenticationHandlerHolder> authHandlerCache;
- private final HashMap<Object, AbstractAuthenticationHandlerHolder[]> handlerMap = new HashMap<Object, AbstractAuthenticationHandlerHolder[]>();
+ private final Map<Object, AbstractAuthenticationHandlerHolder[]> handlerMap = new ConcurrentHashMap<>();
AuthenticationHandlerTracker(
final BundleContext context,
@@ -1740,18 +1740,12 @@
}
// keep a copy of them for unregistration later
- synchronized (handlerMap) {
- handlerMap.put(ref.getProperty(Constants.SERVICE_ID),
- holders);
- }
+ handlerMap.put(ref.getProperty(Constants.SERVICE_ID), holders);
}
}
private void unbindAuthHandler(ServiceReference ref) {
- final AbstractAuthenticationHandlerHolder[] holders;
- synchronized (handlerMap) {
- holders = handlerMap.remove(ref.getProperty(Constants.SERVICE_ID));
- }
+ final AbstractAuthenticationHandlerHolder[] holders = handlerMap.remove(ref.getProperty(Constants.SERVICE_ID));
if (holders != null) {
for (AbstractAuthenticationHandlerHolder holder : holders) {