Adam, all,
I am resurrecting this part of the thread: is it ok if we fix the 10.04 with
the patch below (Adam's commit with rev. 948333 that adds the putIfAbsent
method and then the same fix I committed to 11.04, 12.04 and trunk)?
Thanks,
Jacopo
On May 12, 2012, at 9:17 AM, Jacopo Cappellato wrote:
> As regards the 10.04, what if we backport the putIfAbsent method there and
> then apply a similar fix? Here is the patch I would like to backport:
>
> Index: framework/base/src/org/ofbiz/base/util/cache/test/UtilCacheTests.java
> ===================================================================
> --- framework/base/src/org/ofbiz/base/util/cache/test/UtilCacheTests.java
> (revision 1337449)
> +++ framework/base/src/org/ofbiz/base/util/cache/test/UtilCacheTests.java
> (working copy)
> @@ -323,6 +323,19 @@
> basicTest(cache);
> }
>
> + public void testPutIfAbsent() throws Exception {
> + UtilCache<String, String> cache = createUtilCache(5, 5, 2000, false,
> false);
> + Listener<String, String> gotListener = createListener(cache);
> + Listener<String, String> wantedListener = new Listener<String,
> String>();
> + wantedListener.noteKeyAddition(cache, "two", "dos");
> + assertNull("putIfAbsent", cache.putIfAbsent("two", "dos"));
> + assertHasSingleKey(cache, "two", "dos");
> + assertEquals("putIfAbsent", "dos", cache.putIfAbsent("two",
> "double"));
> + assertHasSingleKey(cache, "two", "dos");
> + cache.removeListener(gotListener);
> + assertEquals("listener", wantedListener, gotListener);
> + }
> +
> public void testChangeMemSize() throws Exception {
> int size = 5;
> long ttl = 2000;
> Index: framework/base/src/org/ofbiz/base/util/cache/UtilCache.java
> ===================================================================
> --- framework/base/src/org/ofbiz/base/util/cache/UtilCache.java
> (revision 1337449)
> +++ framework/base/src/org/ofbiz/base/util/cache/UtilCache.java
> (working copy)
> @@ -289,6 +289,10 @@
> return putInternal(key, value, expireTimeNanos);
> }
>
> + public V putIfAbsent(K key, V value) {
> + return putIfAbsentInternal(key, value, expireTimeNanos);
> + }
> +
> CacheLine<V> createSoftRefCacheLine(final Object key, V value, long
> loadTimeNanos, long expireTimeNanos) {
> return tryRegister(loadTimeNanos, new SoftRefCacheLine<V>(value,
> loadTimeNanos, expireTimeNanos) {
> @Override
> @@ -366,6 +370,10 @@
> return putInternal(key, value,
> TimeUnit.NANOSECONDS.convert(expireTimeMillis, TimeUnit.MILLISECONDS));
> }
>
> + public V putIfAbsent(K key, V value, long expireTimeMillis) {
> + return putIfAbsentInternal(key, value,
> TimeUnit.NANOSECONDS.convert(expireTimeMillis, TimeUnit.MILLISECONDS));
> + }
> +
> V putInternal(K key, V value, long expireTimeNanos) {
> Object nulledKey = fromKey(key);
> CacheLine<V> oldCacheLine = memoryTable.put(nulledKey,
> createCacheLine(key, value, expireTimeNanos));
> @@ -388,6 +396,41 @@
> }
> }
>
> + V putIfAbsentInternal(K key, V value, long expireTimeNanos) {
> + Object nulledKey = fromKey(key);
> + V oldValue;
> + if (fileTable != null) {
> + try {
> + synchronized (this) {
> + oldValue = fileTable.get(nulledKey);
> + if (oldValue == null) {
> + memoryTable.put(nulledKey, createCacheLine(key,
> value, expireTimeNanos));
> + fileTable.put(nulledKey, value);
> + jdbmMgr.commit();
> + }
> + }
> + } catch (IOException e) {
> + Debug.logError(e, module);
> + oldValue = null;
> + }
> + } else {
> + CacheLine<V> newCacheLine = createCacheLine(key, value,
> expireTimeNanos);
> + CacheLine<V> oldCacheLine = memoryTable.putIfAbsent(nulledKey,
> newCacheLine);
> + if (oldCacheLine == null) {
> + oldValue = null;
> + } else {
> + oldValue = oldCacheLine.getValue();
> + cancel(newCacheLine);
> + }
> + }
> + if (oldValue == null) {
> + noteAddition(key, value);
> + return null;
> + } else {
> + return oldValue;
> + }
> + }
> +
> /** Gets an element from the cache according to the specified key.
> * @param key The key for the element, used to reference it in the
> hastables and LRU linked list
> * @return The value of the element specified by the key
> Index: framework/base/src/org/ofbiz/base/util/GroovyUtil.java
> ===================================================================
> --- framework/base/src/org/ofbiz/base/util/GroovyUtil.java (revision
> 1337449)
> +++ framework/base/src/org/ofbiz/base/util/GroovyUtil.java (working copy)
> @@ -102,26 +102,20 @@
>
> public static Class<?> getScriptClassFromLocation(String location) throws
> GeneralException {
> try {
> - Class<?> scriptClass = null;
> - synchronized (parsedScripts) {
> - scriptClass = parsedScripts.get(location);
> - }
> + Class<?> scriptClass = parsedScripts.get(location);
> if (scriptClass == null) {
> URL scriptUrl = FlexibleLocation.resolveLocation(location);
> if (scriptUrl == null) {
> throw new GeneralException("Script not found at location
> [" + location + "]");
> }
> scriptClass = parseClass(scriptUrl.openStream(), location);
> - synchronized (parsedScripts) {
> - Class<?> scriptClassCached = parsedScripts.get(location);
> - if (scriptClassCached == null) {
> - if (Debug.verboseOn()) {
> - Debug.logVerbose("Caching Groovy script at: " +
> location, module);
> - }
> - parsedScripts.put(location, scriptClass);
> - } else {
> - scriptClass = scriptClassCached;
> + Class<?> scriptClassCached =
> parsedScripts.putIfAbsent(location, scriptClass);
> + if (scriptClassCached == null) { // putIfAbsent returns null
> if the class is added
> + if (Debug.verboseOn()) {
> + Debug.logVerbose("Cached Groovy script at: " +
> location, module);
> }
> + } else {
> + scriptClass = scriptClassCached;
> }
> }
> return scriptClass;
> @@ -152,22 +146,16 @@
>
> public static Object runScriptFromClasspath(String script,
> Map<String,Object> context) throws GeneralException {
> try {
> - Class<?> scriptClass = null;
> - synchronized (parsedScripts) {
> - parsedScripts.get(script);
> - }
> + Class<?> scriptClass = parsedScripts.get(script);
> if (scriptClass == null) {
> scriptClass = loadClass(script);
> - synchronized (parsedScripts) {
> - Class<?> scriptClassCached = parsedScripts.get(script);
> - if (scriptClassCached == null) {
> - if (Debug.verboseOn()) {
> - Debug.logVerbose("Caching Groovy script: " +
> script, module);
> - }
> - parsedScripts.put(script, scriptClass);
> - } else {
> - scriptClass = scriptClassCached;
> + Class<?> cachedScriptClass =
> parsedScripts.putIfAbsent(script, scriptClass);
> + if (cachedScriptClass == null) { // putIfAbsent returns null
> if the class is added
> + if (Debug.verboseOn()) {
> + Debug.logVerbose("Cached Groovy script at: " +
> script, module);
> }
> + } else {
> + scriptClass = cachedScriptClass;
> }
> }
> return InvokerHelper.createScript(scriptClass,
> getBinding(context)).run();