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    }