001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2012  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program 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
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.lock.semaphore;
019
020import java.util.concurrent.ConcurrentHashMap;
021import java.util.concurrent.ConcurrentMap;
022import java.util.concurrent.Semaphore;
023import java.util.concurrent.TimeUnit;
024import java.util.concurrent.locks.Condition;
025import java.util.concurrent.locks.Lock;
026import java.util.concurrent.locks.ReadWriteLock;
027
028import net.sf.hajdbc.lock.LockManager;
029
030/**
031 * @author Paul Ferraro
032 */
033public class SemaphoreLockManager implements LockManager
034{
035        private final ConcurrentMap<String, ReadWriteLock> lockMap = new ConcurrentHashMap<String, ReadWriteLock>();
036
037        private final boolean fair;
038        
039        public SemaphoreLockManager(boolean fair)
040        {
041                this.fair = fair;
042        }
043        
044        /**
045         * @see net.sf.hajdbc.lock.LockManager#readLock(java.lang.String)
046         */
047        @Override
048        public Lock readLock(String object)
049        {
050                Lock lock = this.getReadWriteLock(null).readLock();
051                
052                return (object == null) ? lock : new GlobalLock(lock, this.getReadWriteLock(object).readLock());
053        }
054        
055        /**
056         * @see net.sf.hajdbc.lock.LockManager#writeLock(java.lang.String)
057         */
058        @Override
059        public Lock writeLock(String object)
060        {
061                ReadWriteLock readWriteLock = this.getReadWriteLock(null);
062                
063                return (object == null) ? readWriteLock.writeLock() : new GlobalLock(readWriteLock.readLock(), this.getReadWriteLock(object).writeLock());
064        }
065        
066        private synchronized ReadWriteLock getReadWriteLock(String object)
067        {
068                // CHM cannot use a null key
069                String key = (object != null) ? object : "";
070                
071                ReadWriteLock lock = this.lockMap.get(key);
072                
073                if (lock == null)
074                {
075                        lock = new SemaphoreReadWriteLock(new Semaphore(Integer.MAX_VALUE, this.fair));
076
077                        ReadWriteLock existing = this.lockMap.putIfAbsent(key, lock);
078                        
079                        if (existing != null)
080                        {
081                                lock = existing;
082                        }
083                }
084                
085                return lock;
086        }
087        
088        private static class GlobalLock implements Lock
089        {
090                private Lock globalLock;
091                private Lock lock;
092                
093                GlobalLock(Lock globalLock, Lock lock)
094                {
095                        this.globalLock = globalLock;
096                        this.lock = lock;
097                }
098                
099                @Override
100                public void lock()
101                {
102                        this.globalLock.lock();
103                        this.lock.lock();
104                }
105
106                @Override
107                public void lockInterruptibly() throws InterruptedException
108                {
109                        this.globalLock.lockInterruptibly();
110                        
111                        try
112                        {
113                                this.lock.lockInterruptibly();
114                        }
115                        catch (InterruptedException e)
116                        {
117                                this.globalLock.unlock();
118                                throw e;
119                        }
120                }
121
122                @Override
123                public boolean tryLock()
124                {
125                        if (this.globalLock.tryLock())
126                        {
127                                if (this.lock.tryLock())
128                                {
129                                        return true;
130                                }
131
132                                this.globalLock.unlock();
133                        }
134
135                        return false;
136                }
137
138                @Override
139                public boolean tryLock(long time, TimeUnit unit) throws InterruptedException
140                {
141                        if (this.globalLock.tryLock(time, unit))
142                        {
143                                if (this.lock.tryLock(time, unit))
144                                {
145                                        return true;
146                                }
147
148                                this.globalLock.unlock();
149                        }
150
151                        return false;
152                }
153
154                @Override
155                public void unlock()
156                {
157                        this.lock.unlock();
158                        this.globalLock.unlock();
159                }
160
161                @Override
162                public Condition newCondition()
163                {
164                        throw new UnsupportedOperationException();
165                }
166        }
167
168        /**
169         * @see net.sf.hajdbc.Lifecycle#start()
170         */
171        @Override
172        public void start()
173        {
174                // Do nothing
175        }
176
177        /**
178         * @see net.sf.hajdbc.Lifecycle#stop()
179         */
180        @Override
181        public void stop()
182        {
183                // Do nothing
184        }
185}