Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,8 @@ default String getCacheSizeRemark() {
* @param key the object that defines the visibility or scope of the lock.
* @return an existing lock on the specified key or creates a new one if none was found.
*/
@SuppressWarnings("deprecation")
default Lock getLock(final K key) {
return CacheFactory.getLock(key, this);
}
@Nonnull
Lock getLock(final K key);

AtomicBoolean secretKey = new AtomicBoolean(false);
AtomicBoolean secretValue = new AtomicBoolean(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,31 +604,6 @@ public static synchronized void destroyCache(String name) {
}
}

/**
* @deprecated in favour of {@link Cache#getLock}. Will be removed in Openfire 5.0.0.
*
* <p>Returns an existing {@link java.util.concurrent.locks.Lock} on the specified key or creates a new one
* if none was found. This operation is thread safe. Successive calls with the same key may or may not
* return the same {@link java.util.concurrent.locks.Lock}. However, different threads asking for the
* same Lock at the same time will get the same Lock object.<p>
*
* The supplied cache may or may not be used depending whether the server is running on cluster mode
* or not. When not running as part of a cluster then the lock will be unrelated to the cache and will
* only be visible in this JVM.
*
* @param key the object that defines the visibility or scope of the lock.
* @param cache the cache used for holding the lock.
* @return an existing lock on the specified key or creates a new one if none was found.
*/
@Deprecated(since = "4.5", forRemoval = true)
public static synchronized Lock getLock(Object key, Cache cache) {
if (localOnly.contains(cache.getName())) {
return localCacheFactoryStrategy.getLock(key, cache);
} else {
return cacheFactoryStrategy.getLock(key, cache);
}
}

@SuppressWarnings("unchecked")
private static <T extends Cache> T wrapCache(T cache, String name) {
if ("Routing Components Cache".equals(name)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2004-2008 Jive Software, 2017-2019 Ignite Realtime Foundation. All rights reserved.
* Copyright (C) 2004-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,6 @@

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.locks.Lock;

/**
* Implementation of CacheFactory that relies on the specific clustering solution.
Expand Down Expand Up @@ -177,18 +176,6 @@ public interface CacheFactoryStrategy {
*/
void updateCacheStats(Map<String, Cache> caches);

/**
* Returns an existing lock on the specified key or creates a new one if none was found. This
* operation is thread safe. The supplied cache may or may not be used depending whether
* the server is running on cluster mode or not. When not running as part of a cluster then
* the lock will be unrelated to the cache and will only be visible in this JVM.
*
* @param key the object that defines the visibility or scope of the lock.
* @param cache the cache used for holding the lock.
* @return an existing lock on the specified key or creates a new one if none was found.
*/
Lock getLock(Object key, Cache cache);

/**
* Get the plugin name corresponding to this clustering implementation
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2004-2008 Jive Software, 2017-2024 Ignite Realtime Foundation. All rights reserved.
* Copyright (C) 2004-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;

/**
* Acts as a proxy for a Cache implementation. The Cache implementation can be switched on the fly,
Expand Down Expand Up @@ -150,6 +151,12 @@ public Set<K> keySet() {
return Collections.unmodifiableSet(cache.keySet());
}

@Nonnull
@Override
public Lock getLock(K key) {
return cache.getLock(key);
}

@Override
public V get(final Object key) {
return cache.get(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.jivesoftware.util.cache;

import org.jivesoftware.openfire.cluster.ClusteredCacheEntryListener;
import org.jivesoftware.util.cache.lock.LocalLock;

import javax.annotation.Nonnull;
import java.io.Serializable;
Expand All @@ -25,6 +26,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
* Wraps an instance of Ben Manes' Caffeine cache in a class that inherits from
Expand Down Expand Up @@ -431,7 +433,7 @@ public V remove( final Object key )
/**
* Copies all of the mappings from the specified map to this map
* (optional operation). The effect of this call is equivalent to that
* of calling {@link #put(Object, Object) put(k, v)} on this map once
* of calling {@link #put(Serializable, Serializable)} on this map once
* for each mapping from key <tt>k</tt> to value <tt>v</tt> in the
* specified map. The behavior of this operation is undefined if the
* specified map is modified while the operation is in progress.
Expand Down Expand Up @@ -478,6 +480,12 @@ public Set<K> keySet()
return Collections.unmodifiableSet( cache.asMap().keySet() );
}

@Override
@Nonnull
public Lock getLock(K key) {
return LocalLock.getLock(key, this);
}

@Override
public String addClusteredCacheEntryListener(@Nonnull final ClusteredCacheEntryListener<K, V> listener, final boolean includeValues, final boolean includeEventsFromLocalNode) {
throw new UnsupportedOperationException();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2004-2008 Jive Software, 2016-2024 Ignite Realtime Foundation. All rights reserved.
* Copyright (C) 2004-2008 Jive Software, 2016-2025 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LinkedListNode;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.cache.lock.LocalLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -27,6 +28,7 @@
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -65,7 +67,6 @@ public class DefaultCache<K extends Serializable, V extends Serializable> implem
// Contains the set of times when the Cache was last culled
private final Set<Long> cullTimes;


/**
* The map the keys and values are stored in.
*/
Expand Down Expand Up @@ -338,6 +339,12 @@ public Set<K> keySet() {
}
}

@Override
@Nonnull
public Lock getLock(K key) {
return LocalLock.getLock(key, this);
}

/**
* Defines the unit used to calculate the capacity of the cache, which for all instances of this class is byte-size
* based.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,12 @@

package org.jivesoftware.util.cache;

import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.ClusterNodeInfo;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* CacheFactoryStrategy for use in Openfire. It creates and manages local caches, and it's cluster
Expand All @@ -41,13 +32,6 @@
*/
public class DefaultLocalCacheStrategy implements CacheFactoryStrategy {

/**
* Keep track of the locks that are currently being used.
*/
private Map<CacheKey, LockAndCount> locks = new ConcurrentHashMap<>();

private Interner<CacheKey> interner = Interners.newWeakInterner();

public DefaultLocalCacheStrategy() {
}

Expand Down Expand Up @@ -132,140 +116,9 @@ public String getPluginName() {
return "local";
}

@Override
public Lock getLock(Object key, Cache cache) {
return new LocalLock(new CacheKey(cache, key));
}

@SuppressWarnings( "LockAcquiredButNotSafelyReleased" )
private void acquireLock(CacheKey key) {
ReentrantLock lock = lookupLockForAcquire(key);
lock.lock();
}

private void releaseLock(CacheKey key) {
ReentrantLock lock = lookupLockForRelease(key);
lock.unlock();
}

private ReentrantLock lookupLockForAcquire(CacheKey cacheKey) {
CacheKey mutex = interner.intern(cacheKey); // Ensure that the mutex used in the next line is the same for objects that are equal.
synchronized(mutex) {
LockAndCount lac = locks.get(mutex);
if (lac == null) {
lac = new LockAndCount(new ReentrantLock());
lac.count = 1;
locks.put(mutex, lac);
}
else {
lac.count++;
}

return lac.lock;
}
}

private ReentrantLock lookupLockForRelease(CacheKey cacheKey) {
CacheKey mutex = interner.intern(cacheKey); // Ensure that the mutex used in the next line is the same for objects that are equal.
synchronized(mutex) {
LockAndCount lac = locks.get(mutex);
if (lac == null) {
throw new IllegalStateException("No lock found for object " + mutex);
}

if (lac.count <= 1) {
locks.remove(mutex);
}
else {
lac.count--;
}

return lac.lock;
}
}


private class LocalLock implements Lock {
private final CacheKey key;

LocalLock(CacheKey key) {
this.key = key;
}

@Override
public void lock(){
acquireLock(key);
}

@Override
public void unlock() {
releaseLock(key);
}

@Override
public void lockInterruptibly() throws InterruptedException {
ReentrantLock lock = lookupLockForAcquire(key);
lock.lockInterruptibly();
}

@Nonnull
@Override
public Condition newCondition(){
ReentrantLock lock = lookupLockForAcquire(key);
return lock.newCondition();
}

@Override
public boolean tryLock() {
ReentrantLock lock = lookupLockForAcquire(key);
return lock.tryLock();
}

@Override
public boolean tryLock(long time, @Nonnull TimeUnit unit) throws InterruptedException {
ReentrantLock lock = lookupLockForAcquire(key);
return lock.tryLock(time, unit);
}
}

private static class LockAndCount {
final ReentrantLock lock;
int count;

LockAndCount(ReentrantLock lock) {
this.lock = lock;
}
}

@Override
public ClusterNodeInfo getClusterNodeInfo(byte[] nodeID) {
// not clustered
return null;
}

/**
* A key of a cache, namespaced by the cache that it belongs to.
*/
private static class CacheKey {
final String cacheName;
final Object key;

private CacheKey(Cache cache, Object key) {
this.cacheName = cache.getName();
this.key = key;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CacheKey cacheKey = (CacheKey) o;
return cacheName.equals(cacheKey.cacheName) && key.equals(cacheKey.key);
}

@Override
public int hashCode() {
return Objects.hash(cacheName, key);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -368,6 +369,13 @@ public Set<K> keySet()
.collect(Collectors.toSet());
}

@Override
@Nonnull
public Lock getLock(K key) {
final String marshalledKey = marshall(key, keyClass);
return delegate.getLock(marshalledKey);
}

@Override
public CapacityUnit getCapacityUnit() {
return delegate.getCapacityUnit();
Expand Down
Loading