KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > lock > IdentityLock


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.cache.lock;
8
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.jboss.cache.Fqn;
12 import org.jboss.cache.Node;
13 import org.jboss.cache.NodeSPI;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Collection JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.Set JavaDoc;
20 import java.util.concurrent.TimeUnit JavaDoc;
21 import java.util.concurrent.locks.Lock JavaDoc;
22
23 /**
24  * Lock object which grants and releases locks, and associates locks with
25  * <em>owners</em>. The methods to acquire and release a lock require an owner
26  * (Object). When a lock is acquired, we store the current owner with the lock.
27  * When the same owner (<em>but possibly running in a different thread</em>)
28  * wants to acquire the lock, it is immediately granted. When an owner
29  * different from the one currently owning the lock wants to release the lock,
30  * we do nothing (no-op).
31  * <p/>
32  * Consider the following example:
33  * <pre>
34  * FIFOSemaphore lock=new FIFOSemaphore(1);
35  * lock.acquire();
36  * lock.acquire();
37  * lock.release();
38  * </pre>
39  * This would block on the second <tt>acquire()</tt> (although we currently already hold
40  * the lock) because the lock only has 1 permit. So <tt>IdentityLock</tt> will allow the
41  * following code to work properly:
42  * <pre>
43  * IdentityLock lock=new IdentityLock();
44  * lock.readLock().acquire(this, timeout);
45  * lock.writeLock().acquire(this, timeout);
46  * lock.release(this);
47  * </pre>
48  * If the owner is null, then the current thread is taken by default. This allow the following
49  * code to work:
50  * <pre>
51  * IdentityLock lock=new IdentityLock();
52  * lock.readLock().attempt(null, timeout);
53  * lock.writeLock().attempt(null, timeout);
54  * lock.release(null);
55  * </pre>
56  * <br/>
57  * Note that the Object given as owner is required to implement {@link Object#equals}. For
58  * a use case see the examples in the testsuite.
59  *
60  * @author <a HREF="mailto:bela@jboss.org">Bela Ban</a> Apr 11, 2003
61  * @author Ben Wang July 2003
62  * @version $Revision: 1.19 $
63  */

64 public class IdentityLock implements NodeLock
65 {
66
67    /**
68     * Initialized property for debugging "print_lock_details"
69     */

70    private boolean PRINT_LOCK_DETAILS = Boolean.getBoolean("print_lock_details");
71
72    private static final Log log = LogFactory.getLog(IdentityLock.class);
73    private static boolean trace = log.isTraceEnabled();
74    private final LockStrategy lock_;
75    private final LockMap map_;
76    private final boolean mustReacquireRead_;
77    private NodeSPI node;
78
79    /**
80     * Creates a new Identity lock based on the lock level set in the {@link LockStrategyFactory}
81     */

82    public IdentityLock(NodeSPI node)
83    {
84       this(LockStrategyFactory.getLockStrategy(), node);
85       log.trace("Using default lock level");
86    }
87
88    /**
89     * Creates a new IdentityLock based on the lock level in force.
90     *
91     * @param level
92     */

93    public IdentityLock(IsolationLevel level, NodeSPI node)
94    {
95       this(LockStrategyFactory.getLockStrategy(level), node);
96    }
97
98    private IdentityLock(LockStrategy strategy, NodeSPI node)
99    {
100       lock_ = strategy;
101       mustReacquireRead_ = strategy instanceof LockStrategyReadCommitted;
102       map_ = new LockMap();
103       this.node = node;
104    }
105
106    /**
107     * Returns the node for this lock, may be <code>null</code>.
108     */

109    public Node getNode()
110    {
111       return node;
112    }
113
114    /**
115     * Returns the FQN this lock, may be <code>null</code>.
116     */

117    public Fqn getFqn()
118    {
119       if (node == null)
120       {
121          return null;
122       }
123       return node.getFqn();
124    }
125
126    /**
127     * Return a copy of the reader lock owner in List. Size is zero is not available. Note that this list
128     * is synchronized.
129     *
130     * @return Set of readers
131     */

132    public Set JavaDoc getReaderOwners()
133    {
134       return map_.readerOwners();
135    }
136
137    /**
138     * Return the writer lock owner object. Null if not available.
139     *
140     * @return Object owner
141     */

142    public Object JavaDoc getWriterOwner()
143    {
144       return map_.writerOwner();
145    }
146
147    /**
148     * Acquire a write lock with a timeout of <code>timeout</code> milliseconds.
149     * Note that if the current owner owns a read lock, it will be upgraded
150     * automatically. However, if upgrade fails, i.e., timeout, the read lock will
151     * be released automatically.
152     *
153     * @param caller Can't be null.
154     * @param timeout
155     * @return boolean True if lock was acquired and was not held before, false if lock was held
156     * @throws LockingException
157     * @throws TimeoutException
158     */

159    public boolean acquireWriteLock(Object JavaDoc caller, long timeout) throws LockingException, TimeoutException, InterruptedException JavaDoc
160    {
161       if (trace)
162       {
163          log.trace(new StringBuffer JavaDoc("acquiring WL: fqn=").append(getFqn()).append(", caller=").append(caller).
164                  append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
165       }
166       boolean flag = acquireWriteLock0(caller, timeout);
167       if (trace)
168       {
169          log.trace(new StringBuffer JavaDoc("acquired WL: fqn=").append(getFqn()).append(", caller=").append(caller).
170                  append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
171       }
172       return flag;
173    }
174
175    private boolean acquireWriteLock0(Object JavaDoc caller, long timeout) throws LockingException, TimeoutException, InterruptedException JavaDoc
176    {
177       if (caller == null)
178       {
179          throw new IllegalArgumentException JavaDoc("acquireWriteLock(): null caller");
180       }
181
182       if (map_.isOwner(caller, LockMap.OWNER_WRITE))
183       {
184          if (trace)
185          {
186             log.trace("acquireWriteLock(): caller already owns lock for " + getFqn() + " (caller=" + caller + ')');
187          }
188          return false;// owner already has the lock
189
}
190
191       // Check first if we need to upgrade
192
if (map_.isOwner(caller, LockMap.OWNER_READ))
193       {
194          // Currently is a reader owner. Obtain the writer ownership then.
195
Lock JavaDoc wLock;
196          try
197          {
198             if (trace)
199             {
200                log.trace("upgrading RL to WL for " + caller + ", timeout=" + timeout + ", locks: " + map_.printInfo());
201             }
202             wLock = lock_.upgradeLockAttempt(timeout);
203          }
204          catch (UpgradeException ue)
205          {
206             String JavaDoc errStr = "acquireWriteLock(): lock upgrade failed for " + getFqn() + " (caller=" + caller + ')';
207             log.error(errStr, ue);
208             throw new UpgradeException(errStr, ue);
209          }
210          if (wLock == null)
211          {
212             release(caller);// bug fix: remember to release the read lock before throwing the exception
213
map_.removeReader(caller);
214             String JavaDoc errStr = "upgrade lock for " + getFqn() + " could not be acquired after " + timeout + " ms." +
215                     " Lock map ownership " + map_.printInfo() + " (caller=" + caller + ')';
216             log.error(errStr);
217             throw new UpgradeException(errStr);
218          }
219          try
220          {
221             if (trace)
222             {
223                log.trace("upgrading lock for " + getFqn());
224             }
225             map_.upgrade(caller);
226          }
227          catch (OwnerNotExistedException ex)
228          {
229             throw new UpgradeException("Can't upgrade lock to WL for " + getFqn() + ", error in LockMap.upgrade(): " + ex);
230          }
231       }
232       else
233       {
234          // Not a current reader owner. Obtain the writer ownership then.
235
boolean rc = lock_.writeLock().tryLock(timeout, TimeUnit.MILLISECONDS);
236
237          // we don't need to synchronize from here on because we own the semaphore
238
if (!rc)
239          {
240             String JavaDoc errStr = "write lock for " + getFqn() + " could not be acquired after " + timeout + " ms. " +
241                     "Locks: " + map_.printInfo() + " (caller=" + caller + ", lock info: " + toString(true) + ')';
242             log.error(errStr);
243             throw new TimeoutException(errStr);
244          }
245          map_.setWriterIfNotNull(caller);
246       }
247       return true;
248    }
249
250    /**
251     * Acquire a read lock with a timeout period of <code>timeout</code> milliseconds.
252     *
253     * @param caller Can't be null.
254     * @param timeout
255     * @return boolean True if lock was acquired and was not held before, false if lock was held
256     * @throws LockingException
257     * @throws TimeoutException
258     */

259    public boolean acquireReadLock(Object JavaDoc caller, long timeout) throws LockingException, TimeoutException, InterruptedException JavaDoc
260    {
261       if (trace)
262       {
263          log.trace(new StringBuffer JavaDoc("acquiring RL: fqn=").append(getFqn()).append(", caller=").append(caller).
264                  append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
265       }
266       boolean flag = acquireReadLock0(caller, timeout);
267       if (trace)
268       {
269          log.trace(new StringBuffer JavaDoc("acquired RL: fqn=").append(getFqn()).append(", caller=").append(caller).
270                  append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
271       }
272       return flag;
273    }
274
275    private boolean acquireReadLock0(Object JavaDoc caller, long timeout)
276            throws LockingException, TimeoutException, InterruptedException JavaDoc
277    {
278       boolean rc;
279
280       if (caller == null)
281       {
282          throw new IllegalArgumentException JavaDoc("owner is null");
283       }
284
285       boolean hasRead = false;
286       boolean hasRequired = false;
287       if (mustReacquireRead_)
288       {
289          hasRequired = map_.isOwner(caller, LockMap.OWNER_WRITE);
290          if (!hasRequired)
291          {
292             hasRead = map_.isOwner(caller, LockMap.OWNER_READ);
293          }
294       }
295       else if (map_.isOwner(caller, LockMap.OWNER_ANY))
296       {
297          hasRequired = true;
298       }
299
300       if (hasRequired)
301       {
302          if (trace)
303          {
304             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(64);
305             sb.append("acquireReadLock(): caller ").append(caller).append(" already owns lock for ").append(getFqn());
306             log.trace(sb.toString());
307          }
308          return false;// owner already has the lock
309
}
310
311       rc = lock_.readLock().tryLock(timeout, TimeUnit.MILLISECONDS);
312
313       // we don't need to synchronize from here on because we own the semaphore
314
if (!rc)
315       {
316          StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
317          sb.append("read lock for ").append(getFqn()).append(" could not be acquired by ").append(caller);
318          sb.append(" after ").append(timeout).append(" ms. " + "Locks: ").append(map_.printInfo());
319          sb.append(", lock info: ").append(toString(true));
320          log.error(sb.toString());
321          throw new TimeoutException(sb.toString());
322       }
323
324       // Only add to the map if we didn't already have the lock
325
if (!hasRead)
326       {
327          map_.addReader(caller);// this is synchronized internally, we don't need to synchronize here
328
}
329       return true;
330    }
331
332    /**
333     * Release the lock held by the owner.
334     *
335     * @param caller Can't be null.
336     */

337    public void release(Object JavaDoc caller)
338    {
339       if (caller == null)
340       {
341          throw new IllegalArgumentException JavaDoc("IdentityLock.release(): null owner object.");
342       }
343
344       // Check whether to release reader or writer lock.
345
if (map_.isOwner(caller, LockMap.OWNER_READ))
346       {
347          map_.removeReader(caller);
348          lock_.readLock().unlock();
349       }
350       else if (map_.isOwner(caller, LockMap.OWNER_WRITE))
351       {
352          map_.removeWriter();
353          lock_.writeLock().unlock();
354       }
355    }
356
357    /**
358     * Release all locks associated with this instance.
359     */

360    public void releaseAll()
361    {
362       try
363       {
364          if ((map_.writerOwner()) != null)
365          {
366             // lock_.readLock().release();
367
lock_.writeLock().unlock();
368          }
369
370          map_.releaseReaderOwners(lock_);
371       }
372       finally
373       {
374          map_.removeAll();
375       }
376    }
377
378    /**
379     * Same as releaseAll now.
380     */

381    public void releaseForce()
382    {
383       releaseAll();
384    }
385
386    /**
387     * Check if there is a read lock.
388     */

389    public boolean isReadLocked()
390    {
391       return map_.isReadLocked();
392    }
393
394    /**
395     * Check if there is a write lock.
396     */

397    public boolean isWriteLocked()
398    {
399       return map_.writerOwner() != null;
400    }
401
402    /**
403     * Check if there is a read or write lock
404     */

405    public boolean isLocked()
406    {
407       return isReadLocked() || isWriteLocked();
408    }
409
410    /**
411     * Am I a lock owner?
412     *
413     * @param o
414     */

415    public boolean isOwner(Object JavaDoc o)
416    {
417       return map_.isOwner(o, LockMap.OWNER_ANY);
418    }
419
420    public String JavaDoc toString()
421    {
422       return toString(false);
423    }
424
425    public String JavaDoc toString(boolean print_lock_details)
426    {
427       StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
428       toString(sb, print_lock_details);
429       return sb.toString();
430    }
431
432    public void toString(StringBuffer JavaDoc sb)
433    {
434       toString(sb, false);
435    }
436
437    public void toString(StringBuffer JavaDoc sb, boolean print_lock_details)
438    {
439       boolean printed_read_owners = false;
440       Collection JavaDoc read_owners = lock_ != null ? getReaderOwners() : null;
441       if (read_owners != null && read_owners.size() > 0)
442       {
443          // Fix for JBCACHE-310 -- can't just call new ArrayList(read_owners) :(
444
// Creating the ArrayList and calling addAll doesn't work either
445
// Looking at the details of how this is implemented vs. the 2 prev
446
// options, this doesn't look like it should be slower
447
Iterator JavaDoc iter = read_owners.iterator();
448          read_owners = new ArrayList JavaDoc(read_owners.size());
449          while (iter.hasNext())
450          {
451             read_owners.add(iter.next());
452          }
453
454          sb.append("read owners=").append(read_owners);
455          printed_read_owners = true;
456       }
457       else
458       {
459          read_owners = null;
460       }
461
462       Object JavaDoc write_owner = lock_ != null ? getWriterOwner() : null;
463       if (write_owner != null)
464       {
465          if (printed_read_owners)
466          {
467             sb.append(", ");
468          }
469          sb.append("write owner=").append(write_owner);
470       }
471       if (read_owners == null && write_owner == null)
472       {
473          sb.append("<unlocked>");
474       }
475       if (print_lock_details)
476       {
477          sb.append(" (").append(lock_.toString()).append(')');
478       }
479    }
480
481    public boolean acquire(Object JavaDoc caller, long timeout, NodeLock.LockType lock_type) throws LockingException, TimeoutException, InterruptedException JavaDoc
482    {
483       try
484       {
485          if (lock_type == NodeLock.LockType.NONE)
486          {
487             return true;
488          }
489          else if (lock_type == NodeLock.LockType.READ)
490          {
491             return acquireReadLock(caller, timeout);
492          }
493          else
494          {
495             return acquireWriteLock(caller, timeout);
496          }
497       }
498       catch (UpgradeException e)
499       {
500          StringBuffer JavaDoc buf = new StringBuffer JavaDoc("failure upgrading lock: fqn=").append(getFqn()).append(", caller=").append(caller).
501                  append(", lock=").append(toString(true));
502          if (trace)
503          {
504             log.trace(buf.toString());
505          }
506          throw new UpgradeException(buf.toString(), e);
507       }
508       catch (LockingException e)
509       {
510          StringBuffer JavaDoc buf = new StringBuffer JavaDoc("failure acquiring lock: fqn=").append(getFqn()).append(", caller=").append(caller).
511                  append(", lock=").append(toString(true));
512          if (trace)
513          {
514             log.trace(buf.toString());
515          }
516          throw new LockingException(buf.toString(), e);
517       }
518       catch (TimeoutException e)
519       {
520          StringBuffer JavaDoc buf = new StringBuffer JavaDoc("failure acquiring lock: fqn=").append(getFqn()).append(", caller=").append(caller).
521                  append(", lock=").append(toString(true));
522          if (trace)
523          {
524             log.trace(buf.toString());
525          }
526          throw new TimeoutException(buf.toString(), e);
527       }
528    }
529
530    public Set JavaDoc acquireAll(Object JavaDoc caller, long timeout, LockType lock_type)
531            throws LockingException, TimeoutException, InterruptedException JavaDoc
532    {
533       boolean acquired;
534       Set JavaDoc retval = new HashSet JavaDoc();
535
536       if (lock_type == LockType.NONE)
537       {
538          return retval;
539       }
540       acquired = acquire(caller, timeout, lock_type);
541       if (acquired)
542       {
543          retval.add(this);
544       }
545
546       for (NodeSPI n : node.getChildrenDirect())
547       {
548          retval.addAll(n.getLock().acquireAll(caller, timeout, lock_type));
549       }
550       return retval;
551    }
552
553    public void releaseAll(Object JavaDoc owner)
554    {
555       for (NodeSPI n : node.getChildrenDirect())
556       {
557          n.getLock().releaseAll(owner);
558       }
559       release(owner);
560    }
561
562    private void printIndent(StringBuffer JavaDoc sb, int indent)
563    {
564       if (sb != null)
565       {
566          for (int i = 0; i < indent; i++)
567          {
568             sb.append(" ");
569          }
570       }
571    }
572
573    public void printLockInfo(StringBuffer JavaDoc sb, int indent)
574    {
575       boolean locked = isLocked();
576
577       printIndent(sb, indent);
578       sb.append(Fqn.SEPARATOR).append(node.getFqn().getLastElement());
579       if (locked)
580       {
581          sb.append("\t(");
582          toString(sb);
583          sb.append(")");
584       }
585
586       for (NodeSPI n : node.getChildrenDirect())
587       {
588          sb.append("\n");
589          n.getLock().printLockInfo(sb, indent + 4);
590       }
591    }
592
593 }
594
Popular Tags