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