001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.lock; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Map.Entry; 027 028public class KeyLock<K> { 029 030 private Map<Token<K>,SimpleLock<Token<K>>> locks=new HashMap<Token<K>, SimpleLock<Token<K>>>(); 031 032 033 public Lock lock(K key, long timeout) throws LockException { 034 if(timeout<=0) throw new LockException("timeout must be a postive number"); 035 036 SimpleLock<Token<K>> lock; 037 Token<K> token=new Token<K>(key); 038 synchronized (locks) { 039 lock=locks.get(token); 040 if(lock==null) { 041 locks.put(token, lock=new SimpleLock<Token<K>>(token)); 042 } 043 // ignore inner calls with same id 044 else if(lock.getLabel().getThreadId()==token.getThreadId()) { 045 return null; 046 } 047 } 048 lock.lock(timeout); 049 return lock; 050 } 051 052 public void unlock(Lock lock) { 053 if(lock==null) return; 054 055 synchronized (locks) { 056 if(lock.getQueueLength()==0){ 057 locks.remove(((SimpleLock<Token<K>>)lock).getLabel()); 058 } 059 } 060 lock.unlock(); 061 } 062 063 public List<K> getOpenLockNames() { 064 Iterator<Entry<Token<K>, SimpleLock<Token<K>>>> it = locks.entrySet().iterator(); 065 Entry<Token<K>, SimpleLock<Token<K>>> entry; 066 List<K> list=new ArrayList<K>(); 067 while(it.hasNext()){ 068 entry = it.next(); 069 if(entry.getValue().getQueueLength()>0) 070 list.add(entry.getKey().getKey()); 071 } 072 return list; 073 } 074 075 public void clean() { 076 Iterator<Entry<Token<K>, SimpleLock<Token<K>>>> it = locks.entrySet().iterator(); 077 Entry<Token<K>, SimpleLock<Token<K>>> entry; 078 079 while(it.hasNext()){ 080 entry = it.next(); 081 if(entry.getValue().getQueueLength()==0){ 082 synchronized (locks) { 083 if(entry.getValue().getQueueLength()==0){ 084 locks.remove(entry.getKey()); 085 } 086 } 087 } 088 } 089 } 090} 091 092class Token<K> { 093 094 private K key; 095 private long threadid; 096 097 /** 098 * @param key 099 */ 100 public Token(K key) { 101 this.key=key; 102 this.threadid=Thread.currentThread().getId(); 103 } 104 105 /** 106 * @return the id 107 */ 108 public long getThreadId() { 109 return threadid; 110 } 111 public K getKey() { 112 return key; 113 } 114 115 @Override 116 public String toString(){ 117 return key.toString(); 118 } 119 120 @Override 121 public boolean equals(Object obj){ 122 if(obj instanceof Token<?>) { 123 Token<?> other=(Token<?>) obj; 124 obj=other.key; 125 } 126 return key.equals(obj); 127 } 128 129 @Override 130 public int hashCode(){ 131 return key.hashCode(); 132 } 133 134 135} 136 137