001 package org.apache.ojb.broker.locking;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import org.apache.commons.lang.SystemUtils;
019 import org.apache.commons.transaction.locking.GenericLock;
020 import org.apache.commons.transaction.locking.GenericLockManager;
021 import org.apache.commons.transaction.locking.LockException;
022 import org.apache.commons.transaction.util.LoggerFacade;
023 import org.apache.ojb.broker.util.logging.Logger;
024 import org.apache.ojb.broker.util.logging.LoggerFactory;
025
026 /**
027 * A {@link LockManager} implementation based on apache's commons-transaction
028 * locking part.
029 * <p/>
030 * The timeout of locks is currently (OJB 1.0.2) not supported, maybe
031 * in further versions.
032 *
033 * @author <a href="mailto:arminw@apache.org">Armin Waibel</a>
034 * @version $Id: LockManagerCommonsImpl.java,v 1.1 2007-08-24 22:17:41 ewestfal Exp $
035 */
036 public class LockManagerCommonsImpl implements LockManager
037 {
038 private Logger log = LoggerFactory.getLogger(LockManagerCommonsImpl.class);
039
040 /**
041 * Timeout of the obtained lock.
042 */
043 private long lockTimeout;
044 /**
045 * Time to wait when lock call is blocked.
046 */
047 private long blockTimeout;
048 private LoggerFacade logFacade;
049 private OJBLockManager lm;
050
051 public LockManagerCommonsImpl()
052 {
053 logFacade = new LoggerFacadeImpl();
054 // default lock timeout
055 this.lockTimeout = DEFAULT_LOCK_TIMEOUT;
056 // default time to wait for a lock
057 this.blockTimeout = DEFAULT_BLOCK_TIMEOUT;
058 lm = new OJBLockManager(logFacade, blockTimeout, GenericLockManager.DEFAULT_CHECK_THRESHHOLD);
059 }
060
061 private boolean ignore(int isolationLevel)
062 {
063 return isolationLevel == IsolationLevels.IL_OPTIMISTIC || isolationLevel == IsolationLevels.IL_NONE;
064 }
065
066 public long getLockTimeout()
067 {
068 return lockTimeout;
069 }
070
071 public void setLockTimeout(long timeout)
072 {
073 this.lockTimeout = timeout;
074 }
075
076 public long getBlockTimeout()
077 {
078 return blockTimeout;
079 }
080
081 public void setBlockTimeout(long blockTimeout)
082 {
083 this.blockTimeout = blockTimeout;
084 }
085
086 public String getLockInfo()
087 {
088 String eol = SystemUtils.LINE_SEPARATOR;
089 StringBuffer msg = new StringBuffer("Class: " + LockManagerCommonsImpl.class.getName() + eol);
090 msg.append("lock timeout: " + getLockTimeout() + " [ms]" + eol);
091 msg.append("block timeout: " + getBlockTimeout() + " [ms]" + eol);
092 msg.append("commons-tx lock-manger info ==> " + eol);
093 msg.append(lm);
094 return msg.toString();
095 }
096
097 public boolean readLock(Object key, Object resourceId, int isolationLevel)
098 {
099 return ignore(isolationLevel) ? true : lm.readLock(key, resourceId, new Integer(isolationLevel), blockTimeout);
100 }
101
102 public boolean writeLock(Object key, Object resourceId, int isolationLevel)
103 {
104 return ignore(isolationLevel) ? true : lm.writeLock(key, resourceId, new Integer(isolationLevel), blockTimeout);
105 }
106
107 public boolean upgradeLock(Object key, Object resourceId, int isolationLevel)
108 {
109 return ignore(isolationLevel) ? true : lm.upgradeLock(key, resourceId, new Integer(isolationLevel), blockTimeout);
110 }
111
112 public boolean releaseLock(Object key, Object resourceId)
113 {
114 boolean result = true;
115 try
116 {
117 lm.release(key, resourceId);
118 }
119 catch(RuntimeException e)
120 {
121 log.error("Can't release lock for owner key " + key + ", on resource " + resourceId, e);
122 result = false;
123 }
124 return result;
125 }
126
127 public void releaseLocks(Object key)
128 {
129 try
130 {
131 lm.releaseAll(key);
132 }
133 catch(RuntimeException e)
134 {
135 log.error("Can't release all locks for owner key " + key, e);
136 }
137 }
138
139 public boolean hasRead(Object key, Object resourceId)
140 {
141 return lm.hasRead(key, resourceId);
142 }
143
144 public boolean hasWrite(Object key, Object resourceId)
145 {
146 return lm.hasWrite(key, resourceId);
147 }
148
149 public boolean hasUpgrade(Object key, Object resourceId)
150 {
151 return lm.hasUpgrade(key, resourceId);
152 }
153
154
155 //===================================================
156 // inner class, commons-tx lock manager
157 //===================================================
158 /**
159 * Extension class of {@link CommonsOJBLockManager}
160 * which supports additionally convenience methods for read/write/upgrade locks and checks
161 * for these locks.
162 */
163 final class OJBLockManager extends CommonsOJBLockManager
164 {
165 public OJBLockManager(LoggerFacade logger, long timeoutMSecs, long checkThreshholdMSecs)
166 throws IllegalArgumentException
167 {
168 super(logger, timeoutMSecs, checkThreshholdMSecs);
169 }
170
171 private CommonsOJBLockManager.OJBLock lookupLock(Object resourceId)
172 {
173 return (CommonsOJBLockManager.OJBLock) getLock(resourceId);
174 }
175
176 boolean readLock(Object key, Object resourceId, Integer isolationLevel, long timeout)
177 {
178 /*
179 arminw: Not sure what's the best way to go
180 - a normal 'lock' call with enabled lock wait time (blocking)
181 - or an immediately returning 'tryLock' call.
182 E.g. assume the user query for 1000 objects and 100 objects has write locks
183 by concurrent threads, then blocking could be counterproductive, because it could
184 be that the app will wait seconds for the first read lock, get it, wait seconds
185 for the next read lock,.... In the worst case app will wait for all 100 locked objects.
186 So I chose the 'tryLock' call for read locks which immediately return.
187 */
188 int lockLevel = mapLockLevelDependendOnIsolationLevel(isolationLevel, COMMON_READ_LOCK);
189 return tryLock(key, resourceId, lockLevel, true, isolationLevel);
190 }
191
192 boolean writeLock(Object key, Object resourceId, Integer isolationLevel, long timeout)
193 {
194 try
195 {
196 int lockLevel = mapLockLevelDependendOnIsolationLevel(isolationLevel, COMMON_WRITE_LOCK);
197 lock(key, resourceId, lockLevel, GenericLock.COMPATIBILITY_REENTRANT,
198 false, timeout, isolationLevel);
199 return true;
200 }
201 catch(LockException e)
202 {
203 if(log.isEnabledFor(Logger.INFO)) log.info("Can't get write lock for " + key, e);
204 return false;
205 }
206 }
207
208 boolean upgradeLock(Object key, Object resourceId, Integer isolationLevel, long timeout)
209 {
210 try
211 {
212 int lockLevel = mapLockLevelDependendOnIsolationLevel(isolationLevel, COMMON_UPGRADE_LOCK);
213 lock(key, resourceId, lockLevel, GenericLock.COMPATIBILITY_REENTRANT,
214 false, timeout, isolationLevel);
215 return true;
216 }
217 catch(LockException e)
218 {
219 if(log.isEnabledFor(Logger.INFO)) log.info("Can't get upgrade lock for " + key, e);
220 return false;
221 }
222 }
223
224 boolean hasRead(Object key, Object resourceId)
225 {
226 CommonsOJBLockManager.OJBLock lock = lookupLock(resourceId);
227 boolean result = false;
228 if(lock != null)
229 {
230 result = lock.hasRead(key);
231 }
232 return result;
233 }
234
235 boolean hasWrite(Object key, Object resourceId)
236 {
237 CommonsOJBLockManager.OJBLock lock = lookupLock(resourceId);
238 boolean result = false;
239 if(lock != null)
240 {
241 result = lock.hasWrite(key);
242 }
243 return result;
244 }
245
246 boolean hasUpgrade(Object key, Object resourceId)
247 {
248 CommonsOJBLockManager.OJBLock lock = lookupLock(resourceId);
249 boolean result = false;
250 if(lock != null)
251 {
252 result = lock.hasUpgrade(key);
253 }
254 return result;
255 }
256 }
257
258
259 //===================================================
260 // inner class, logging facade
261 //===================================================
262 /**
263 * Logging facade for apache's commons-transaction.
264 */
265 final class LoggerFacadeImpl implements LoggerFacade
266 {
267
268 public LoggerFacade createLogger(String name)
269 {
270 return this;
271 }
272
273 public void logInfo(String message)
274 {
275 log.info(message);
276 }
277
278 public void logFine(String message)
279 {
280 log.debug(message);
281 }
282
283 public boolean isFineEnabled()
284 {
285 return log.isDebugEnabled();
286 }
287
288 public void logFiner(String message)
289 {
290 log.debug(message);
291 }
292
293 public boolean isFinerEnabled()
294 {
295 return log.isDebugEnabled();
296 }
297
298 public void logFinest(String message)
299 {
300 log.debug(message);
301 }
302
303 public boolean isFinestEnabled()
304 {
305 return log.isDebugEnabled();
306 }
307
308 public void logWarning(String message)
309 {
310 log.warn(message);
311 }
312
313 public void logWarning(String message, Throwable t)
314 {
315 log.warn(message, t);
316 }
317
318 public void logSevere(String message)
319 {
320 log.error(message);
321 }
322
323 public void logSevere(String message, Throwable t)
324 {
325 log.error(message, t);
326 }
327 }
328 }