/* * Copyright 2009 VoidSearch.com * * 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 com.voidsearch.voidbase.apps.cache; import com.voidsearch.voidbase.apps.cache.containers.CacheValue; import com.voidsearch.voidbase.apps.cache.containers.CacheLockType; import com.voidsearch.voidbase.util.GenericUtil; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * CacheModuleLockStrategy handles atomicity of cache operations based on cache handler configuration */ public class CacheModuleLockStrategy { private static final Map<String, ReentrantLock> locks = new ConcurrentHashMap<String, ReentrantLock>(); protected static final Logger logger = LoggerFactory.getLogger(CacheModuleLockStrategy.class.getName()); /** * Creates a new instance of CacheModuleLockStrategy */ public CacheModuleLockStrategy() { } /** * Executes cache operation with respect to operation's atomicity and proxies * it down to specific executors (atomic, global lock, etc.) * @param handler * @param operation * @param route * @param params * @param key * @param value * @return a response from operation * @throws CacheException */ protected CacheValue execute(VoidBaseCache handler, String operation, List<String> route, Map<String, String> params, String key, String value) throws CacheException { CacheLockType lockType = handler.getLockType(operation); try { if (lockType == CacheLockType.ATOMIC) return executeAtomicLockOperation(handler, operation, route, params, key, value); if (lockType == CacheLockType.GLOBAL) return executeGlobalLockOperation(handler, operation, route, params, key, value); if (lockType == CacheLockType.NONE || lockType == CacheLockType.DEFAULT) return executeOperation(handler, operation, route, params, key, value); } catch (InterruptedException e) { logger.error("Operation interrupted: " + operation); GenericUtil.logException(e); throw new CacheException("Operation interrupted: " + operation); } throw new CacheException("Cache execute lock strategy failed - unknown lock type"); } /** * Executes cache operation with respect to operation's atomicity and proxies * it down to specific executors (atomic, global lock, etc.) * @param handler * @param operation * @param name * @param key * @param value * @return * @throws CacheException */ protected CacheValue execute(VoidBaseCache handler, String operation, String name, String key, String value) throws CacheException { CacheLockType lockType = handler.getLockType(operation); try { if (lockType == CacheLockType.ATOMIC) return executeAtomicLockOperation(handler, operation, name, key, value); if (lockType == CacheLockType.GLOBAL) return executeGlobalLockOperation(handler, operation, name, key, value); if (lockType == CacheLockType.NONE || lockType == CacheLockType.DEFAULT) return executeOperation(handler, operation, name, key, value); } catch (InterruptedException e) { logger.error("Operation interrupted: " + operation); GenericUtil.logException(e); throw new CacheException("Operation interrupted: " + operation); } throw new CacheException("Cache execute lock strategy failed - unknown lock type"); } /** * Executes atomic operation on cache * @param handler * @param operation * @param route * @param params * @param key * @param value * @return a response from operation * @throws CacheException * @throws InterruptedException */ protected synchronized CacheValue executeAtomicLockOperation(VoidBaseCache handler, String operation, List<String> route, Map<String, String> params, String key, String value) throws CacheException, InterruptedException { CacheValue result; // create lock synchronized(locks) { if (!locks.containsKey(key)) { locks.put(key, new ReentrantLock()); } } // execute operation locks.get(key).lock(); try { result = handler.process(operation, route, params, key, value); } finally { synchronized(locks) { if (locks.containsKey(key)) { locks.get(key).unlock(); locks.remove(key); } } } return result; } /** * Executes atomic operation on cache * @param handler * @param operation * @param name * @param key * @param value * @return * @throws CacheException * @throws InterruptedException */ protected synchronized CacheValue executeAtomicLockOperation(VoidBaseCache handler, String operation, String name, String key, String value) throws CacheException, InterruptedException { CacheValue result; // create lock synchronized(locks) { if (!locks.containsKey(key)) { locks.put(key, new ReentrantLock()); } } // execute operation locks.get(key).lock(); try { result = handler.process(operation, name, key, value); } finally { synchronized(locks) { if (locks.containsKey(key)) { locks.get(key).unlock(); locks.remove(key); } } } return result; } /** * Executes globally locked operation on cache * @param handler * @param operation * @param route * @param params * @param key * @param value * @return a response from operation * @throws CacheException */ protected CacheValue executeGlobalLockOperation(VoidBaseCache handler, String operation, List<String> route, Map<String, String> params, String key, String value) throws CacheException { CacheValue result; synchronized(handler) { result = handler.process(operation, route, params, key, value); } return result; } protected CacheValue executeGlobalLockOperation(VoidBaseCache handler, String operation, String name, String key, String value) throws CacheException { CacheValue result; synchronized(handler) { result = handler.process(operation, name, key, value); } return result; } /** * Executes operation on cache without any locking * @param handler * @param operation * @param route * @param params * @param key * @param value * @return a response from operation * @throws CacheException */ protected CacheValue executeOperation(VoidBaseCache handler, String operation, List<String> route, Map<String, String> params, String key, String value) throws CacheException { return handler.process(operation, route, params, key, value); } /** * Executes operation on cache without any locking * @param handler * @param operation * @param name * @param key * @param value * @return * @throws CacheException */ protected CacheValue executeOperation(VoidBaseCache handler, String operation, String name, String key, String value) throws CacheException { return handler.process(operation, name, key, value); } }