Skip to content

Commit 2b868ea

Browse files
author
Ravi Nadahar
committed
Rearrange
Signed-off-by: Ravi Nadahar <nadahar@rediffmail.com>
1 parent c2c9584 commit 2b868ea

2 files changed

Lines changed: 69 additions & 46 deletions

File tree

bundles/org.openhab.core.automation.module.script.rulesupport/src/main/java/org/openhab/core/automation/module/script/rulesupport/internal/CacheScriptExtension.java

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
*/
1313
package org.openhab.core.automation.module.script.rulesupport.internal;
1414

15+
import java.lang.reflect.InvocationTargetException;
16+
import java.lang.reflect.Method;
1517
import java.util.Collection;
1618
import java.util.HashMap;
1719
import java.util.HashSet;
@@ -52,6 +54,7 @@ public class CacheScriptExtension implements ScriptExtensionProvider {
5254
static final String SHARED_CACHE_NAME = "sharedCache";
5355
static final String PRIVATE_CACHE_NAME = "privateCache";
5456
static final String OBJECT_CACHE_NAME = "objectCache";
57+
static final String LOCKING_CACHE_NAME = "lockingCache";
5558

5659
private final Logger logger = LoggerFactory.getLogger(CacheScriptExtension.class);
5760
private final ScheduledExecutorService scheduler = ThreadPoolManager
@@ -60,6 +63,7 @@ public class CacheScriptExtension implements ScriptExtensionProvider {
6063
private final Lock cacheLock = new ReentrantLock();
6164
private final Map<String, Object> sharedCache = new HashMap<>();
6265
private final ObjectCache objectCache = new ObjectCacheImpl();
66+
private final LockingCache lockingCache = new LockingCacheImpl();
6367
private final Map<String, Set<String>> sharedCacheKeyAccessors = new ConcurrentHashMap<>();
6468

6569
private final Map<String, ValueCacheImpl> privateCaches = new ConcurrentHashMap<>();
@@ -79,7 +83,7 @@ public Collection<String> getPresets() {
7983

8084
@Override
8185
public Collection<String> getTypes() {
82-
return Set.of(PRIVATE_CACHE_NAME, SHARED_CACHE_NAME, OBJECT_CACHE_NAME);
86+
return Set.of(PRIVATE_CACHE_NAME, SHARED_CACHE_NAME, OBJECT_CACHE_NAME, LOCKING_CACHE_NAME);
8387
}
8488

8589
@Override
@@ -91,6 +95,8 @@ public Collection<String> getTypes() {
9195
return privateCaches.computeIfAbsent(scriptIdentifier, ValueCacheImpl::new);
9296
case OBJECT_CACHE_NAME:
9397
return objectCache;
98+
case LOCKING_CACHE_NAME:
99+
return lockingCache;
94100
default:
95101
return null;
96102
}
@@ -102,7 +108,7 @@ public Map<String, Object> importPreset(String scriptIdentifier, String preset)
102108
Object privateCache = Objects
103109
.requireNonNull(privateCaches.computeIfAbsent(scriptIdentifier, ValueCacheImpl::new));
104110
return Map.of(SHARED_CACHE_NAME, new TrackingValueCacheImpl(scriptIdentifier), PRIVATE_CACHE_NAME,
105-
privateCache, OBJECT_CACHE_NAME, objectCache);
111+
privateCache, OBJECT_CACHE_NAME, objectCache, LOCKING_CACHE_NAME, lockingCache);
106112
}
107113

108114
return Map.of();
@@ -344,33 +350,6 @@ private static class LockingCacheImpl implements LockingCache {
344350
// All access must be guarded by "cache"
345351
private final Map<String, LockingCacheValue> cache = new HashMap<>();
346352

347-
@Override
348-
public @Nullable Object put(String key, Object object) {
349-
LockingCacheValue value;
350-
boolean created = false;
351-
synchronized (cache) {
352-
value = cache.get(key);
353-
if (value == null || value.removed) {
354-
created = true;
355-
value = new LockingCacheValue();
356-
value.lock.lock();
357-
cache.put(key, value);
358-
}
359-
}
360-
Object result;
361-
if (!created) {
362-
value.lock.lock();
363-
}
364-
try {
365-
result = value.object;
366-
value.object = object;
367-
} finally {
368-
value.lock.unlock();
369-
}
370-
371-
return result;
372-
}
373-
374353
@Override
375354
public @Nullable Object lockAndPut(String key, Object object) {
376355
LockingCacheValue value;
@@ -453,13 +432,13 @@ public Object lockAndGet(String key, Supplier<Object> supplier) {
453432
}
454433

455434
@Override
456-
public void unlock(String key) {
435+
public @Nullable Object unlock(String key) {
457436
LockingCacheValue value;
458437
synchronized (cache) {
459438
value = cache.get(key);
460439
}
461440
if (value == null) {
462-
return;
441+
return null;
463442
}
464443
if (value.removed) {
465444
unlockAll(value);
@@ -470,6 +449,18 @@ public void unlock(String key) {
470449
// Ignore
471450
}
472451
}
452+
if (value.object instanceof Cloneable c) {
453+
try {
454+
Method method = c.getClass().getMethod("clone");
455+
if (method.canAccess(c)) {
456+
return method.invoke(c);
457+
}
458+
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
459+
| InvocationTargetException e) {
460+
// Ignore, just return null
461+
}
462+
}
463+
return null;
473464
}
474465

475466
private void unlockAll(LockingCacheValue value) {

bundles/org.openhab.core.automation.module.script.rulesupport/src/main/java/org/openhab/core/automation/module/script/rulesupport/shared/LockingCache.java

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@
2929
public interface LockingCache {// TODO: (Nad) Redo JavaDocs
3030

3131
/**
32-
* Add a new key-value-pair to the cache. If the key is already present, the old value is replaces by the new value.
32+
* Add a new key-value-pair to the cache or replace an existing value.
33+
* <p>
34+
* When returning,a lock is held on the object, which <b>must</b> be released with a subsequent call
35+
* to {@link #unlock(String)} with the same key. Failure to do so will result in a deadlock if this
36+
* entry is attempted accessed in the future.
3337
*
34-
* @param key a {@link String} used as key
35-
* @param object the {@code Object} to store with the key
36-
* @return the old object associated with this key or {@code null} if the key didn't exist
38+
* @param key the {@link String} used as a key.
39+
* @param object the object to store with the key
40+
* @return The old object associated with the key or {@code null}.
3741
*/
38-
@Nullable
39-
Object put(String key, Object object);
40-
4142
@Nullable
4243
Object lockAndPut(String key, Object object);
4344

@@ -51,22 +52,53 @@ public interface LockingCache {// TODO: (Nad) Redo JavaDocs
5152
Object remove(String key);
5253

5354
/**
54-
* Get a object from the cache
55+
* Get an object from the cache.
56+
* <p>
57+
* When returning,a lock is held on the object, which <b>must</b> be released with a subsequent call
58+
* to {@link #unlock(String)} with the same key. Failure to do so will result in a deadlock if this
59+
* entry is attempted accessed in the future.
5560
*
56-
* @param key the key of the requested object
57-
* @return the object associated with the key or {@code null}.
61+
* @param key the {@link String} used as a key.
62+
* @return The object associated with the key or {@code null}.
5863
*/
5964
@Nullable
6065
Object lockAndGet(String key);
6166

6267
/**
63-
* Get a serialized object from the cache or create a new key-value-pair from the given supplier
68+
* Get an object from the cache or create a one for the key with the given supplier.
69+
* <p>
70+
* When returning,a lock is held on the object, which <b>must</b> be released with a subsequent call
71+
* to {@link #unlock(String)} with the same key. Failure to do so will result in a deadlock if this
72+
* entry is attempted accessed in the future.
6473
*
65-
* @param key the key of the requested serialized object
66-
* @param supplier a supplier that returns a non-null serialized object to be used if the key isn't present
67-
* @return the serialized object associated with the key
74+
* @param key the {@link String} used as a key.
75+
* @param supplier the supplier that returns a non-{@code null} object to be used if the key isn't present.
76+
* @return The object associated with the key.
6877
*/
6978
Object lockAndGet(String key, Supplier<Object> supplier);
7079

71-
void unlock(String key);
80+
/**
81+
* Unlock the value for the specified key and release the local reference to the cached instance.
82+
* <p>
83+
* To make sure that no reference is held to the cached instance, this method should always be used
84+
* like this:
85+
*
86+
* <pre>
87+
* <code>
88+
* object = cache.lockAndGet("key");
89+
* try {
90+
* ..
91+
* (do something with the object)
92+
* ..
93+
* } finally {
94+
* object = cache.release("key");
95+
* }
96+
* </code>
97+
* </pre>
98+
*
99+
* @param key
100+
* @return
101+
*/
102+
@Nullable
103+
Object unlock(String key);
72104
}

0 commit comments

Comments
 (0)