Prevent potential incorrect cache lookup due to hash collisions (#4214)

* Prevent potential incorrect cache lookup due to hash collisions
Signed-off-by: Jörg Sautter <joerg.sautter@gmx.net>
This commit is contained in:
joerg1985 2024-05-05 09:39:34 +02:00 committed by GitHub
parent 7efdd44197
commit 6aed435c77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -12,7 +12,6 @@
*/ */
package org.openhab.core.thing.internal; package org.openhab.core.thing.internal;
import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -28,7 +27,6 @@ import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.cache.ExpiringCacheMap;
import org.openhab.core.common.AbstractUID; import org.openhab.core.common.AbstractUID;
import org.openhab.core.common.SafeCaller; import org.openhab.core.common.SafeCaller;
import org.openhab.core.common.registry.RegistryChangeListener; import org.openhab.core.common.registry.RegistryChangeListener;
@ -98,6 +96,9 @@ import org.slf4j.LoggerFactory;
@Component(service = { EventSubscriber.class, CommunicationManager.class }, immediate = true) @Component(service = { EventSubscriber.class, CommunicationManager.class }, immediate = true)
public class CommunicationManager implements EventSubscriber, RegistryChangeListener<ItemChannelLink> { public class CommunicationManager implements EventSubscriber, RegistryChangeListener<ItemChannelLink> {
private record CacheKey(String type, Profile profile, Thing thing) {
}
private static final Profile NO_OP_PROFILE = new Profile() { private static final Profile NO_OP_PROFILE = new Profile() {
private final ProfileTypeUID noOpProfileUID = new ProfileTypeUID(ProfileTypeUID.SYSTEM_SCOPE, "noop"); private final ProfileTypeUID noOpProfileUID = new ProfileTypeUID(ProfileTypeUID.SYSTEM_SCOPE, "noop");
@ -112,9 +113,6 @@ public class CommunicationManager implements EventSubscriber, RegistryChangeList
} }
}; };
// how long to cache profile safe call instances
private static final Duration CACHE_EXPIRATION = Duration.ofMinutes(30);
// the timeout to use for any item event processing // the timeout to use for any item event processing
public static final long THINGHANDLER_EVENT_TIMEOUT = TimeUnit.SECONDS.toMillis(30); public static final long THINGHANDLER_EVENT_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
@ -132,7 +130,7 @@ public class CommunicationManager implements EventSubscriber, RegistryChangeList
private final SafeCaller safeCaller; private final SafeCaller safeCaller;
private final ThingRegistry thingRegistry; private final ThingRegistry thingRegistry;
private final ExpiringCacheMap<Integer, Profile> profileSafeCallCache = new ExpiringCacheMap<>(CACHE_EXPIRATION); private final ConcurrentHashMap<CacheKey, Profile> profileSafeCallCache = new ConcurrentHashMap<>();
@Activate @Activate
public CommunicationManager(final @Reference AutoUpdateManager autoUpdateManager, public CommunicationManager(final @Reference AutoUpdateManager autoUpdateManager,
@ -352,10 +350,10 @@ public class CommunicationManager implements EventSubscriber, RegistryChangeList
} }
private void applyProfileForUpdate(Profile profile, Thing thing, State convertedState) { private void applyProfileForUpdate(Profile profile, Thing thing, State convertedState) {
int key = Objects.hash("UPDATE", profile, thing); CacheKey key = new CacheKey("UPDATE", profile, thing);
Profile p = profileSafeCallCache.putIfAbsentAndGet(key, () -> safeCaller.create(profile, Profile.class) // Profile p = profileSafeCallCache.computeIfAbsent(key, (k) -> safeCaller.create(k.profile, Profile.class) //
.withAsync() // .withAsync() //
.withIdentifier(thing) // .withIdentifier(k.thing) //
.withTimeout(THINGHANDLER_EVENT_TIMEOUT) // .withTimeout(THINGHANDLER_EVENT_TIMEOUT) //
.build()); .build());
if (p != null) { if (p != null) {
@ -366,12 +364,12 @@ public class CommunicationManager implements EventSubscriber, RegistryChangeList
} }
private void applyProfileForCommand(Profile profile, Thing thing, Command convertedCommand) { private void applyProfileForCommand(Profile profile, Thing thing, Command convertedCommand) {
if (profile instanceof StateProfile stateProfile) { if (profile instanceof StateProfile) {
int key = Objects.hash("COMMAND", profile, thing); CacheKey key = new CacheKey("COMMAND", profile, thing);
Profile p = profileSafeCallCache.putIfAbsentAndGet(key, Profile p = profileSafeCallCache.computeIfAbsent(key,
() -> safeCaller.create(stateProfile, StateProfile.class) // (k) -> safeCaller.create((StateProfile) k.profile, StateProfile.class) //
.withAsync() // .withAsync() //
.withIdentifier(thing) // .withIdentifier(k.thing) //
.withTimeout(THINGHANDLER_EVENT_TIMEOUT) // .withTimeout(THINGHANDLER_EVENT_TIMEOUT) //
.build()); .build());
if (p instanceof StateProfile profileP) { if (p instanceof StateProfile profileP) {