001    package railo.commons.lock;
002    
003    import java.util.ArrayList;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    import java.util.List;
007    import java.util.Map;
008    import java.util.Map.Entry;
009    
010    public class KeyLock<K> {
011    
012            private Map<Token<K>,SimpleLock<Token<K>>> locks=new HashMap<Token<K>, SimpleLock<Token<K>>>();
013            
014            
015            public Lock lock(K key, long timeout) throws LockException, LockInterruptedException {
016                    if(timeout<=0) throw new LockException("timeout must be a postive number");
017                    
018                    SimpleLock<Token<K>> lock;
019                    Token<K> token=new Token<K>(key);
020                    synchronized (locks) {
021                            lock=locks.get(token);
022                            if(lock==null) {
023                                    locks.put(token, lock=new SimpleLock<Token<K>>(token));
024                            }
025                            // ignore inner calls with same id
026                            else if(lock.getLabel().getThreadId()==token.getThreadId()) {
027                                    return null;
028                            }
029                    }
030                    lock.lock(timeout);
031                    return lock;
032            }
033    
034            public void unlock(Lock lock) {
035                    if(lock==null) return;
036                    
037                    synchronized (locks) {
038                            if(lock.getQueueLength()==0){
039                                    locks.remove(((SimpleLock<Token<K>>)lock).getLabel());
040                            }
041                    }
042                    lock.unlock();
043            }
044            
045            public List<K> getOpenLockNames() {
046                    Iterator<Entry<Token<K>, SimpleLock<Token<K>>>> it = locks.entrySet().iterator();
047                    Entry<Token<K>, SimpleLock<Token<K>>> entry;
048                    List<K> list=new ArrayList<K>();
049                    while(it.hasNext()){
050                            entry = it.next();
051                            if(entry.getValue().getQueueLength()>0)
052                                    list.add(entry.getKey().getKey());
053                    }
054                    return list;
055            }
056    
057            public void clean() {
058                    Iterator<Entry<Token<K>, SimpleLock<Token<K>>>> it = locks.entrySet().iterator();
059                    Entry<Token<K>, SimpleLock<Token<K>>> entry;
060                    
061                    while(it.hasNext()){
062                            entry = it.next();
063                            if(entry.getValue().getQueueLength()==0){
064                                    synchronized (locks) {
065                                            if(entry.getValue().getQueueLength()==0){
066                                                    locks.remove(entry.getKey());
067                                            }
068                                    }
069                            }
070                    }
071            }
072    }
073    
074    class Token<K> {
075    
076            private K key;
077            private long threadid;
078    
079            /**
080             * @param key
081             */
082            public Token(K key) {
083                    this.key=key;
084                    this.threadid=Thread.currentThread().getId();
085            }
086    
087            /**
088             * @return the id
089             */
090            public long getThreadId() {
091                    return threadid;
092            }
093            public K getKey() {
094                    return key;
095            }
096    
097            /**
098             * @see java.lang.Object#toString()
099             */
100            public String toString(){
101                    return key.toString();
102            }
103            
104            /**
105             * @see java.lang.Object#equals(java.lang.Object)
106             */
107            public boolean equals(Object obj){
108                    if(obj instanceof Token<?>) {
109                            Token<?> other=(Token<?>) obj;
110                            obj=other.key;
111                    }
112                    return key.equals(obj);
113            }
114            
115            /**
116             * @see java.lang.Object#hashCode()
117             */
118            public int hashCode(){
119                    return key.hashCode();
120            }
121            
122            
123    }
124    
125