View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.orchestra.lib;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  
25  /**
26   * A reentrant mutual exclusion with the same basic
27   * behavior and semantics as the implicit monitor lock accessed using
28   * {@code synchronized} methods and statements.
29   * <p>
30   * Serialization of this class behaves in the same way as built-in
31   * locks: a deserialized lock is in the unlocked state, regardless of
32   * its state when serialized.
33   * <p>
34   * This class exists just for the purposes of Java 1.4 compatibility;
35   * it is equivalent to the Java 1.5 ReentrantLock class. It probably
36   * doesn't perform as well as the "real" lock class, but Orchestra
37   * doesn't use it in any critical paths.
38   * 
39   * @since 1.1
40   */
41  public class _ReentrantLock implements java.io.Serializable
42  {
43      private static final long serialVersionUID = 7373984872572414699L;
44      private static final long WAIT_WARN_MILLIS = 30L * 1000L; // 30 seconds
45  
46      private final Log log = LogFactory.getLog(_ReentrantLock.class);
47      private transient Thread lockedBy;
48      private int lockCount;
49      
50      public void lockInterruptibly() throws InterruptedException
51      {
52          Thread caller = Thread.currentThread();
53          for(;;)
54          {
55              synchronized(this)
56              {
57                  if (lockedBy == null)
58                  {
59                      lockedBy = caller;
60                      lockCount = 1;
61                      return;
62                  } 
63      
64                  if (lockedBy == caller)
65                  {
66                      ++lockCount;
67                      return;
68                  }
69  
70                  long waitedFor;
71                  try
72                  {
73                      if (log.isDebugEnabled())
74                      {
75                          log.debug("Waiting for lock " + this.toString()
76                                  + " which is owned by thread "
77                                  + lockedBy.getName());
78                      }
79                      long beganAt = System.currentTimeMillis();
80                      this.wait(WAIT_WARN_MILLIS);
81                      waitedFor = System.currentTimeMillis() - beganAt;
82                  }
83                  catch(InterruptedException e)
84                  {
85                      throw e;
86                  }
87                  
88                  if (waitedFor >= WAIT_WARN_MILLIS)
89                  {
90                      // Hopefully this will not get invoked very often!
91                      if (log.isWarnEnabled())
92                      {
93                          String lockedByName;
94                          if (lockedBy != null)
95                          {
96                              lockedByName = lockedBy.getName();
97                          }
98                          else
99                          {
100                             // Unlikely but possible due to race conditions
101                             lockedByName = "(none)";
102                         }
103                         log.warn("Waited for longer than " + WAIT_WARN_MILLIS 
104                             + " milliseconds for access to lock " + this.toString()
105                             + " which is locked by thread " + lockedByName);
106                     }
107                 }
108             }
109         }
110     }
111     
112     public void unlock()
113     {
114         Thread caller = Thread.currentThread();
115         synchronized(this)
116         {
117             if (lockedBy != caller)
118             {
119                 throw new IllegalStateException("Unlock on lock not owned by caller");
120             }
121             
122             --lockCount;
123             if (lockCount == 0)
124             {
125                 lockedBy = null;
126                 this.notifyAll();
127             }
128         }
129     }
130     
131     public boolean isHeldByCurrentThread()
132     {
133         Thread caller = Thread.currentThread();
134         synchronized(this)
135         {
136             return lockedBy == caller;
137         }
138     }
139 }