001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.tm;
023:
024: import javax.naming.InitialContext;
025: import javax.naming.NamingException;
026: import javax.transaction.SystemException;
027: import javax.transaction.Transaction;
028: import javax.transaction.TransactionManager;
029:
030: /**
031: * A TransactionLocal is similar to ThreadLocal except it is keyed on the
032: * Transactions. A transaction local variable is cleared after the transaction
033: * completes.
034: *
035: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
036: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
037: * @version $Revision: 57208 $
038: */
039: public class TransactionLocal {
040:
041: /**
042: * To simplify null values handling in the preloaded data pool we use
043: * this value instead of 'null'
044: */
045: private static final Object NULL_VALUE = new Object();
046:
047: /**
048: * The transaction manager is maintained by the system and
049: * manges the assocation of transaction to threads.
050: */
051: protected final TransactionManager transactionManager;
052:
053: /**
054: * The delegate
055: */
056: protected TransactionLocalDelegate delegate;
057:
058: /**
059: * Creates a thread local variable.
060: * @throws IllegalStateException if there is no system transaction manager
061: */
062: public TransactionLocal() {
063: try {
064: InitialContext context = new InitialContext();
065: transactionManager = (TransactionManager) context
066: .lookup("java:/TransactionManager");
067: } catch (NamingException e) {
068: throw new IllegalStateException(
069: "An error occured while looking up the transaction manager: "
070: + e);
071: }
072: initDelegate();
073: }
074:
075: /**
076: * Creates a transaction local variable. Using the given transaction manager
077: *
078: * @param tm the transaction manager
079: */
080: public TransactionLocal(TransactionManager tm) {
081: if (tm == null)
082: throw new IllegalArgumentException(
083: "Null transaction manager");
084: this .transactionManager = tm;
085: initDelegate();
086: }
087:
088: /**
089: * Lock the TransactionLocal using the current transaction<p>
090: *
091: * WARN: The current implemention just "locks the transactions"
092: *
093: * @throws IllegalStateException if the transaction is not active
094: * @throws InterruptedException if the thread is interrupted
095: */
096: public void lock() throws InterruptedException {
097: lock(getTransaction());
098: }
099:
100: /**
101: * Lock the TransactionLocal using the provided transaction<p>
102: *
103: * WARN: The current implemention just "locks the transactions"
104: *
105: * @param transaction the transaction
106: * @throws IllegalStateException if the transaction is not active
107: * @throws InterruptedException if the thread is interrupted
108: */
109: public void lock(Transaction transaction)
110: throws InterruptedException {
111: // ignore when there is no transaction
112: if (transaction == null)
113: return;
114:
115: delegate.lock(this , transaction);
116: }
117:
118: /**
119: * Unlock the TransactionLocal using the current transaction
120: */
121: public void unlock() {
122: unlock(getTransaction());
123: }
124:
125: /**
126: * Unlock the ThreadLocal using the provided transaction
127: *
128: * @param transaction the transaction
129: */
130: public void unlock(Transaction transaction) {
131: // ignore when there is no transaction
132: if (transaction == null)
133: return;
134:
135: delegate.unlock(this , transaction);
136: }
137:
138: /**
139: * Returns the initial value for this thransaction local. This method
140: * will be called once per accessing transaction for each TransactionLocal,
141: * the first time each transaction accesses the variable with get or set.
142: * If the programmer desires TransactionLocal variables to be initialized to
143: * some value other than null, TransactionLocal must be subclassed, and this
144: * method overridden. Typically, an anonymous inner class will be used.
145: * Typical implementations of initialValue will call an appropriate
146: * constructor and return the newly constructed object.
147: *
148: * @return the initial value for this TransactionLocal
149: */
150: protected Object initialValue() {
151: return null;
152: }
153:
154: /**
155: * get the transaction local value.
156: */
157: protected Object getValue(Transaction tx) {
158: return delegate.getValue(this , tx);
159: }
160:
161: /**
162: * put the value in the TransactionImpl map
163: */
164: protected void storeValue(Transaction tx, Object value) {
165: delegate.storeValue(this , tx, value);
166: }
167:
168: /**
169: * does Transaction contain object?
170: */
171: protected boolean containsValue(Transaction tx) {
172: return delegate.containsValue(this , tx);
173: }
174:
175: /**
176: * Returns the value of this TransactionLocal variable associated with the
177: * thread context transaction. Creates and initializes the copy if this is
178: * the first time the method is called in a transaction.
179: *
180: * @return the value of this TransactionLocal
181: */
182: public Object get() {
183: return get(getTransaction());
184: }
185:
186: /**
187: * Returns the value of this TransactionLocal variable associated with the
188: * specified transaction. Creates and initializes the copy if this is the
189: * first time the method is called in a transaction.
190: *
191: * @param transaction the transaction for which the variable it to
192: * be retrieved
193: * @return the value of this TransactionLocal
194: * @throws IllegalStateException if an error occures while registering
195: * a synchronization callback with the transaction
196: */
197: public Object get(Transaction transaction) {
198: if (transaction == null)
199: return initialValue();
200:
201: Object value = getValue(transaction);
202:
203: // is we didn't get a value initalize this object with initialValue()
204: if (value == null) {
205: // get the initial value
206: value = initialValue();
207:
208: // if value is null replace it with the null value standin
209: if (value == null) {
210: value = NULL_VALUE;
211: }
212:
213: // store the value
214: storeValue(transaction, value);
215: }
216:
217: // if the value is the null standin return null
218: if (value == NULL_VALUE) {
219: return null;
220: }
221:
222: // finall return the value
223: return value;
224: }
225:
226: /**
227: * Sets the value of this TransactionLocal variable associtated with the
228: * thread context transaction. This is only used to change the value from
229: * the one assigned by the initialValue method, and many applications will
230: * have no need for this functionality.
231: *
232: * @param value the value to be associated with the thread context
233: * transactions's TransactionLocal
234: */
235: public void set(Object value) {
236: set(getTransaction(), value);
237: }
238:
239: /**
240: * Sets the value of this TransactionLocal variable associtated with the
241: * specified transaction. This is only used to change the value from
242: * the one assigned by the initialValue method, and many applications will
243: * have no need for this functionality.
244: *
245: * @param transaction the transaction for which the value will be set
246: * @param value the value to be associated with the thread context
247: * transactions's TransactionLocal
248: */
249: public void set(Transaction transaction, Object value) {
250: if (transaction == null)
251: throw new IllegalStateException("there is no transaction");
252: // If this transaction is unknown, register for synchroniztion callback,
253: // and call initialValue to give subclasses a chance to do some
254: // initialization.
255: if (!containsValue(transaction)) {
256: initialValue();
257: }
258:
259: // if value is null replace it with the null value standin
260: if (value == null) {
261: value = NULL_VALUE;
262: }
263:
264: // finally store the value
265: storeValue(transaction, value);
266: }
267:
268: public Transaction getTransaction() {
269: try {
270: return transactionManager.getTransaction();
271: } catch (SystemException e) {
272: throw new IllegalStateException(
273: "An error occured while getting the "
274: + "transaction associated with the current thread: "
275: + e);
276: }
277: }
278:
279: /**
280: * Initialise the delegate
281: */
282: protected void initDelegate() {
283: if (transactionManager instanceof TransactionLocalDelegate)
284: delegate = (TransactionLocalDelegate) transactionManager;
285: else
286: delegate = new TransactionLocalDelegateImpl(
287: transactionManager);
288: }
289: }
|