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}