Index: CachingNamespaceResolver.java
===================================================================
--- CachingNamespaceResolver.java (revision 489007)
+++ CachingNamespaceResolver.java (working copy)
@@ -30,6 +30,10 @@
import javax.jcr.NamespaceException;
import java.util.Map;
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
+import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
+
/**
* Implements a {@link NamespaceResolver} that caches QName to resolved jcr names
* and vice versa. The cache is invalidated when a namespace uri to prefix
@@ -54,6 +58,11 @@
private final Map jcrNameToQName;
/**
+ * ReadWriteLock to synchronize access to the cache maps.
+ */
+ private final ReadWriteLock rwLock = new WriterPreferenceReadWriteLock();
+
+ /**
* Creates a new CachingNamespaceResolver.
*
* @param base a base namespace resolver with support for listener
@@ -62,8 +71,8 @@
*/
public CachingNamespaceResolver(AbstractNamespaceResolver base, int cacheSize) {
this.base = base;
- qnameToJCRName = new LRUMap(cacheSize);
- jcrNameToQName = new LRUMap(cacheSize);
+ qnameToJCRName = new ConcurrentLRUMap(cacheSize);
+ jcrNameToQName = new ConcurrentLRUMap(cacheSize);
this.base.addListener(this);
}
@@ -109,31 +118,71 @@
/**
* @inheritDoc
*/
- public synchronized QName retrieveName(String jcrName) {
- return (QName) jcrNameToQName.get(jcrName);
+ public QName retrieveName(String jcrName) {
+ Sync readLock = rwLock.readLock();
+ try {
+ readLock.acquire();
+ } catch (InterruptedException e) {
+ return null;
+ }
+ try {
+ return (QName) jcrNameToQName.get(jcrName);
+ } finally {
+ readLock.release();
+ }
}
/**
* @inheritDoc
*/
- public synchronized String retrieveName(QName name) {
- return (String) qnameToJCRName.get(name);
+ public String retrieveName(QName name) {
+ Sync readLock = rwLock.readLock();
+ try {
+ readLock.acquire();
+ } catch (InterruptedException e) {
+ return null;
+ }
+ try {
+ return (String) qnameToJCRName.get(name);
+ } finally {
+ readLock.release();
+ }
}
/**
* @inheritDoc
*/
- public synchronized void cacheName(String jcrName, QName name) {
- qnameToJCRName.put(name, jcrName);
- jcrNameToQName.put(jcrName, name);
+ public void cacheName(String jcrName, QName name) {
+ Sync writeLock = rwLock.writeLock();
+ try {
+ writeLock.acquire();
+ } catch (InterruptedException e) {
+ return;
+ }
+ try {
+ qnameToJCRName.put(name, jcrName);
+ jcrNameToQName.put(jcrName, name);
+ } finally {
+ writeLock.release();
+ }
}
/**
* @inheritDoc
*/
- public synchronized void evictAllNames() {
- qnameToJCRName.clear();
- jcrNameToQName.clear();
+ public void evictAllNames() {
+ Sync writeLock = rwLock.writeLock();
+ try {
+ writeLock.acquire();
+ } catch (InterruptedException e) {
+ return;
+ }
+ try {
+ qnameToJCRName.clear();
+ jcrNameToQName.clear();
+ } finally {
+ writeLock.release();
+ }
}
//----------------------------------------------------< NamespaceListener >
@@ -161,4 +210,35 @@
public void namespaceRemoved(String uri) {
evictAllNames();
}
+
+ //----------------------------------------------------------
+
+ /**
+ * Extends the default Commons Collection LRUMap and synchronizes the method
+ * {@link #moveToMRU} which is the only method that modifies the internal
+ * data structures when a lookup is done on the LRUMap (e.g. a get()). This
+ * allows to use the read methods of this implementation from multiple
+ * threads concurrently with only little synchronization overhead.
+ */
+ private static final class ConcurrentLRUMap extends LRUMap {
+
+ /**
+ * Creates a new ConcurrentLRUMap with a given
+ * maxSize.
+ *
+ * @param maxSize the max size.
+ */
+ public ConcurrentLRUMap(int maxSize) {
+ super(maxSize);
+ }
+
+ /**
+ * The method of this implementation is synchronized.
+ *
+ * @param entry the link entry.
+ */
+ protected synchronized void moveToMRU(LinkEntry entry) {
+ super.moveToMRU(entry);
+ }
+ }
}