KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > quartz > impl > jdbcjobstore > JTANonClusteredSemaphore


1 /*
2  * Copyright 2004-2006 OpenSymphony
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  *
16  */

17 package org.quartz.impl.jdbcjobstore;
18
19 import java.sql.Connection JavaDoc;
20 import java.util.HashSet JavaDoc;
21
22 import javax.naming.InitialContext JavaDoc;
23 import javax.naming.NamingException JavaDoc;
24 import javax.transaction.Synchronization JavaDoc;
25 import javax.transaction.SystemException JavaDoc;
26 import javax.transaction.Transaction JavaDoc;
27 import javax.transaction.TransactionManager JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 /**
33  * Provides in memory thread/resource locking that is JTA
34  * <code>{@link javax.transaction.Transaction}</code> aware.
35  * It is most appropriate for use when using
36  * <code>{@link org.quartz.impl.jdbcjobstore.JobStoreCMT}</code> without clustering.
37  *
38  * <p>
39  * This <code>Semaphore</code> implementation is <b>not</b> Quartz cluster safe.
40  * </p>
41  *
42  * <p>
43  * When a lock is obtained/released but there is no active JTA
44  * <code>{@link javax.transaction.Transaction}</code>, then this <code>Semaphore</code> operates
45  * just like <code>{@link org.quartz.impl.jdbcjobstore.SimpleSemaphore}</code>.
46  * </p>
47  *
48  * <p>
49  * By default, this class looks for the <code>{@link javax.transaction.TransactionManager}</code>
50  * in JNDI under name "java:TransactionManager". If this is not where your Application Server
51  * registers it, you can modify the JNDI lookup location using the
52  * "transactionManagerJNDIName" property.
53  * </p>
54  *
55  * <p>
56  * <b>IMPORTANT:</b> This Semaphore implementation is currently experimental.
57  * It has been tested a limited amount on JBoss 4.0.3SP1. If you do choose to
58  * use it, any feedback would be most appreciated!
59  * </p>
60  *
61  * @see org.quartz.impl.jdbcjobstore.SimpleSemaphore
62  */

63 public class JTANonClusteredSemaphore implements Semaphore {
64
65     /*
66      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67      *
68      * Data members.
69      *
70      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
71      */

72
73     public static final String JavaDoc DEFAULT_TRANSACTION_MANANGER_LOCATION = "java:TransactionManager";
74
75     ThreadLocal JavaDoc lockOwners = new ThreadLocal JavaDoc();
76
77     HashSet JavaDoc locks = new HashSet JavaDoc();
78
79     private final Log log = LogFactory.getLog(getClass());
80
81     private String JavaDoc transactionManagerJNDIName = DEFAULT_TRANSACTION_MANANGER_LOCATION;
82     
83     
84     /*
85      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
86      *
87      * Interface.
88      *
89      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90      */

91
92     protected Log getLog() {
93         return log;
94     }
95
96     public void setTransactionManagerJNDIName(String JavaDoc transactionManagerJNDIName) {
97         this.transactionManagerJNDIName = transactionManagerJNDIName;
98     }
99     
100     private HashSet JavaDoc getThreadLocks() {
101         HashSet JavaDoc threadLocks = (HashSet JavaDoc) lockOwners.get();
102         if (threadLocks == null) {
103             threadLocks = new HashSet JavaDoc();
104             lockOwners.set(threadLocks);
105         }
106         return threadLocks;
107     }
108
109     /**
110      * Grants a lock on the identified resource to the calling thread (blocking
111      * until it is available).
112      *
113      * @return true if the lock was obtained.
114      */

115     public synchronized boolean obtainLock(Connection JavaDoc conn, String JavaDoc lockName) throws LockException {
116
117         lockName = lockName.intern();
118
119         Log log = getLog();
120
121         if(log.isDebugEnabled()) {
122             log.debug(
123                 "Lock '" + lockName + "' is desired by: "
124                         + Thread.currentThread().getName());
125         }
126
127         if (!isLockOwner(conn, lockName)) {
128             if(log.isDebugEnabled()) {
129                 log.debug(
130                     "Lock '" + lockName + "' is being obtained: "
131                             + Thread.currentThread().getName());
132             }
133             
134             while (locks.contains(lockName)) {
135                 try {
136                     this.wait();
137                 } catch (InterruptedException JavaDoc ie) {
138                     if(log.isDebugEnabled()) {
139                         log.debug(
140                             "Lock '" + lockName + "' was not obtained by: "
141                                     + Thread.currentThread().getName());
142                     }
143                 }
144             }
145
146             // If we are in a transaction, register a callback to actually release
147
// the lock when the transaction completes
148
Transaction JavaDoc t = getTransaction();
149             if (t != null) {
150                 try {
151                     t.registerSynchronization(new SemaphoreSynchronization(lockName));
152                 } catch (Exception JavaDoc e) {
153                     throw new LockException("Failed to register semaphore with Transaction.", e);
154                 }
155             }
156             
157             if(log.isDebugEnabled()) {
158                 log.debug(
159                     "Lock '" + lockName + "' given to: "
160                             + Thread.currentThread().getName());
161             }
162             
163             
164             getThreadLocks().add(lockName);
165             locks.add(lockName);
166         } else if(log.isDebugEnabled()) {
167             log.debug(
168                 "Lock '" + lockName + "' already owned by: "
169                         + Thread.currentThread().getName()
170                         + " -- but not owner!",
171                 new Exception JavaDoc("stack-trace of wrongful returner"));
172         }
173
174         return true;
175     }
176
177     /**
178      * Helper method to get the current <code>{@link javax.transaction.Transaction}</code>
179      * from the <code>{@link javax.transaction.TransactionManager}</code> in JNDI.
180      *
181      * @return The current <code>{@link javax.transaction.Transaction}</code>, null if
182      * not currently in a transaction.
183      */

184     protected Transaction JavaDoc getTransaction() throws LockException{
185         InitialContext JavaDoc ic = null;
186         try {
187             ic = new InitialContext JavaDoc();
188             TransactionManager JavaDoc tm = (TransactionManager JavaDoc)ic.lookup(transactionManagerJNDIName);
189             
190             return tm.getTransaction();
191         } catch (SystemException JavaDoc e) {
192             throw new LockException("Failed to get Transaction from TransactionManager", e);
193         } catch (NamingException JavaDoc e) {
194             throw new LockException("Failed to find TransactionManager in JNDI under name: " + transactionManagerJNDIName, e);
195         } finally {
196             if (ic != null) {
197                 try {
198                     ic.close();
199                 } catch (NamingException JavaDoc ignored) {
200                 }
201             }
202         }
203     }
204     
205     /**
206      * Release the lock on the identified resource if it is held by the calling
207      * thread, unless currently in a JTA transaction.
208      */

209     public synchronized void releaseLock(Connection JavaDoc conn, String JavaDoc lockName) throws LockException {
210         releaseLock(lockName, false);
211     }
212     
213     /**
214      * Release the lock on the identified resource if it is held by the calling
215      * thread, unless currently in a JTA transaction.
216      *
217      * @param fromSynchronization True if this method is being invoked from
218      * <code>{@link Synchronization}</code> notified of the enclosing
219      * transaction having completed.
220      *
221      * @throws LockException Thrown if there was a problem accessing the JTA
222      * <code>Transaction</code>. Only relevant if <code>fromSynchronization</code>
223      * is false.
224      */

225     protected synchronized void releaseLock(
226         String JavaDoc lockName, boolean fromSynchronization) throws LockException {
227         lockName = lockName.intern();
228
229         if (isLockOwner(null, lockName)) {
230             
231             if (fromSynchronization == false) {
232                 Transaction JavaDoc t = getTransaction();
233                 if (t != null) {
234                     if(getLog().isDebugEnabled()) {
235                         getLog().debug(
236                             "Lock '" + lockName + "' is in a JTA transaction. " +
237                             "Return deferred by: " + Thread.currentThread().getName());
238                     }
239                     
240                     // If we are still in a transaction, then we don't want to
241
// actually release the lock.
242
return;
243                 }
244             }
245             
246             if(getLog().isDebugEnabled()) {
247                 getLog().debug(
248                     "Lock '" + lockName + "' returned by: "
249                             + Thread.currentThread().getName());
250             }
251             getThreadLocks().remove(lockName);
252             locks.remove(lockName);
253             this.notify();
254         } else if (getLog().isDebugEnabled()) {
255             getLog().debug(
256                 "Lock '" + lockName + "' attempt to return by: "
257                         + Thread.currentThread().getName()
258                         + " -- but not owner!",
259                 new Exception JavaDoc("stack-trace of wrongful returner"));
260         }
261     }
262
263     /**
264      * Determine whether the calling thread owns a lock on the identified
265      * resource.
266      */

267     public synchronized boolean isLockOwner(Connection JavaDoc conn, String JavaDoc lockName) {
268         lockName = lockName.intern();
269
270         return getThreadLocks().contains(lockName);
271     }
272
273     /**
274      * This Semaphore implementation does not use the database.
275      */

276     public boolean requiresConnection() {
277         return false;
278     }
279
280     /**
281      * Helper class that is registered with the active
282      * <code>{@link javax.transaction.Transaction}</code> so that the lock
283      * will be released when the transaction completes.
284      */

285     private class SemaphoreSynchronization implements Synchronization JavaDoc {
286         private String JavaDoc lockName;
287         
288         public SemaphoreSynchronization(String JavaDoc lockName) {
289             this.lockName = lockName;
290         }
291         
292         public void beforeCompletion() {
293             // nothing to do...
294
}
295     
296         public void afterCompletion(int status) {
297             try {
298                 releaseLock(lockName, true);
299             } catch (LockException e) {
300                 // Ignore as can't be thrown with fromSynchronization set to true
301
}
302         }
303     }
304 }
305
Popular Tags