001    package railo.commons.lock.rw;
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    import railo.commons.lock.Lock;
011    import railo.commons.lock.LockException;
012    import railo.commons.lock.LockInterruptedException;
013    import railo.runtime.exp.PageRuntimeException;
014    import railo.runtime.op.Caster;
015    
016    public class RWKeyLock<K> {
017    
018            private Map<K,RWLock<K>> locks=new HashMap<K,RWLock<K>>();
019            
020            public Lock lock(K token, long timeout, boolean readOnly) throws LockException, LockInterruptedException {
021                    if(timeout<=0) throw new LockException("timeout must be a postive number");
022                    
023                    RWWrap<K> wrap;
024                    //K token=key;
025                    synchronized (locks) {
026                            RWLock<K> lock;
027                            lock=locks.get(token);
028                            if(lock==null) {
029                                    locks.put(token, lock=new RWLock<K>(token));
030                            }
031                            lock.inc();
032                            wrap= new RWWrap<K>(lock, readOnly);
033                    }
034                    try{
035                            wrap.lock(timeout);
036                    }
037                    catch(LockException e){
038                            synchronized (locks) {wrap.getLock().dec();}
039                            throw e;
040                    }
041                    catch(LockInterruptedException e){
042                            synchronized (locks) {wrap.getLock().dec();}
043                            throw e;
044                    }
045                    catch(Throwable t){
046                            synchronized (locks) {wrap.getLock().dec();}
047                            throw new PageRuntimeException(Caster.toPageException(t));
048                    }
049                    return wrap;
050            }
051    
052            public void unlock(Lock lock) {
053                    if(!(lock instanceof RWWrap)) {
054                            return;
055                    }
056                    
057                    lock.unlock();
058                    
059                    synchronized (locks) {
060                            ((RWWrap)lock).getLock().dec();
061                            if(lock.getQueueLength()==0){
062                                    locks.remove(((RWWrap)lock).getLabel());
063                            }
064                    }
065            }
066            
067            public List<K> getOpenLockNames() {
068                    Iterator<Entry<K, RWLock<K>>> it = locks.entrySet().iterator();
069                    Entry<K, RWLock<K>> entry;
070                    List<K> list=new ArrayList<K>();
071                    while(it.hasNext()){
072                            entry = it.next();
073                            if(entry.getValue().getQueueLength()>0)
074                                    list.add(entry.getKey());
075                    }
076                    return list;
077            }
078    
079            public void clean() {
080                    Iterator<Entry<K, RWLock<K>>> it = locks.entrySet().iterator();
081                    Entry<K, RWLock<K>> entry;
082                    
083                    while(it.hasNext()){
084                            entry = it.next();
085                            if(entry.getValue().getQueueLength()==0){
086                                    synchronized (locks) {
087                                            if(entry.getValue().getQueueLength()==0){
088                                                    locks.remove(entry.getKey());
089                                            }
090                                    }
091                            }
092                    }
093            }
094    }
095    
096    class RWWrap<L> implements Lock {
097            
098            private RWLock<L> lock;
099            private boolean readOnly;
100    
101            public RWWrap(RWLock<L> lock, boolean readOnly){
102                    this.lock=lock;
103                    this.readOnly=readOnly;
104            }
105    
106            public void lock(long timeout) throws LockException, LockInterruptedException {
107                    lock.lock(timeout, readOnly);
108            }
109    
110            public void unlock() {
111                    lock.unlock(readOnly);
112            }
113            public int getQueueLength() {
114                    return lock.getQueueLength();
115            }
116    
117            public L getLabel(){
118                    return lock.getLabel();
119            }
120    
121            public RWLock<L> getLock(){
122                    return lock;
123            }
124            public boolean isReadOnly(){
125                    return readOnly;
126            }
127            
128    }