/* * Copyright (C) 2005-2008 Jive Software. 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.util.cache; import org.jivesoftware.openfire.cluster.ClusterNodeInfo; import java.util.Collection; import java.util.Collections; import java.util.Map; 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 * related method implementations do nothing. * * @see Cache * @see CacheFactory */ public class DefaultLocalCacheStrategy implements CacheFactoryStrategy { /** * Keep track of the locks that are currently being used. */ private Map<Object, LockAndCount> locks = new ConcurrentHashMap<>(); public DefaultLocalCacheStrategy() { } @Override public boolean startCluster() { return false; } @Override public void stopCluster() { } @Override public Cache createCache(String name) { // Get cache configuration from system properties or default (hardcoded) values long maxSize = CacheFactory.getMaxCacheSize(name); long lifetime = CacheFactory.getMaxCacheLifetime(name); // Create cache with located properties return new DefaultCache(name, maxSize, lifetime); } @Override public void destroyCache(Cache cache) { cache.clear(); } @Override public boolean isSeniorClusterMember() { return true; } @Override public Collection<ClusterNodeInfo> getClusterNodesInfo() { return Collections.emptyList(); } @Override public int getMaxClusterNodes() { return 0; } @Override public byte[] getSeniorClusterMemberID() { return null; } @Override public byte[] getClusterMemberID() { return new byte[0]; } @Override public long getClusterTime() { return System.currentTimeMillis(); } @Override public void doClusterTask(final ClusterTask task) { } @Override public void doClusterTask(ClusterTask task, byte[] nodeID) { throw new IllegalStateException("Cluster service is not available"); } @Override public Collection<Object> doSynchronousClusterTask(ClusterTask task, boolean includeLocalMember) { return Collections.emptyList(); } @Override public Object doSynchronousClusterTask(ClusterTask task, byte[] nodeID) { throw new IllegalStateException("Cluster service is not available"); } @Override public void updateCacheStats(Map<String, Cache> caches) { } @Override public String getPluginName() { return "local"; } @Override public Lock getLock(Object key, Cache cache) { Object lockKey = key; if (key instanceof String) { lockKey = ((String) key).intern(); } return new LocalLock(lockKey); } private void acquireLock(Object key) { ReentrantLock lock = lookupLockForAcquire(key); lock.lock(); } private void releaseLock(Object key) { ReentrantLock lock = lookupLockForRelease(key); lock.unlock(); } private ReentrantLock lookupLockForAcquire(Object key) { synchronized(key) { LockAndCount lac = locks.get(key); if (lac == null) { lac = new LockAndCount(new ReentrantLock()); lac.count = 1; locks.put(key, lac); } else { lac.count++; } return lac.lock; } } private ReentrantLock lookupLockForRelease(Object key) { synchronized(key) { LockAndCount lac = locks.get(key); if (lac == null) { throw new IllegalStateException("No lock found for object " + key); } if (lac.count <= 1) { locks.remove(key); } else { lac.count--; } return lac.lock; } } private class LocalLock implements Lock { private final Object key; LocalLock(Object key) { this.key = key; } @Override public void lock(){ acquireLock(key); } @Override public void unlock() { releaseLock(key); } @Override public void lockInterruptibly(){ throw new UnsupportedOperationException(); } @Override public Condition newCondition(){ throw new UnsupportedOperationException(); } @Override public boolean tryLock() { throw new UnsupportedOperationException(); } @Override public boolean tryLock(long time, TimeUnit unit) { throw new UnsupportedOperationException(); } } 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; } }