001: /*
002: * $Header: /export/home/cvsroot/MyPersonalizerRepository/MyPersonalizer/Subsystems/Kernel/Sources/es/udc/mypersonalizer/kernel/model/repository/sql/plain/Transaction.java,v 1.1.1.1 2004/03/25 12:08:36 fbellas Exp $
003: * $Revision: 1.1.1.1 $
004: * $Date: 2004/03/25 12:08:36 $
005: *
006: * =============================================================================
007: *
008: * Copyright (c) 2003, The MyPersonalizer Development Group
009: * (http://www.tic.udc.es/~fbellas/mypersonalizer/index.html) at
010: * University Of A Coruna
011: * All rights reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions are met:
015: *
016: * - Redistributions of source code must retain the above copyright notice,
017: * this list of conditions and the following disclaimer.
018: *
019: * - Redistributions in binary form must reproduce the above copyright notice,
020: * this list of conditions and the following disclaimer in the documentation
021: * and/or other materials provided with the distribution.
022: *
023: * - Neither the name of the University Of A Coruna nor the names of its
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
028: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
029: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
030: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
031: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
032: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
033: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
034: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
035: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
036: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
037: * POSSIBILITY OF SUCH DAMAGE.
038: *
039: */
040:
041: package es.udc.mypersonalizer.kernel.model.repository.sql.plain;
042:
043: import java.util.Map;
044: import java.sql.Connection;
045: import java.sql.SQLException;
046:
047: import es.udc.mypersonalizer.kernel.util.exceptions.InternalErrorException;
048:
049: /**
050: * This class represents a database transaction managed by a
051: * <code>TransactionManager</code>. Currently, this transaction
052: * embraces a unique <code>Connection</code>.
053: * <p>
054: * Each transaction is owned by an Object. In this implementation
055: * only the owner is allowed to perform mantaining operations on
056: * the transaction, that is, release and rollback.
057: * <p>
058: * This owner object are unique for each <code>Thread</code>, and
059: * it is obtained from the <code>ThreadAttachmentManager</code>.
060: *
061: * @author Fernando Bellas
062: * @see es.udc.mypersonalizer.kernel.model.repository.sql.plain.ThreadAttachmentManager
063: * @since 1.0
064: */
065: public class Transaction {
066:
067: /**
068: * SUPPORT transaction demarcation.
069: * It has the same meaning than the one for EJBs.
070: */
071: public static final int TRANSACTION_SUPPORTS = 1;
072:
073: /**
074: * REQUIRED transaction demarcation.
075: * It has the same meaning than the one for EJBs.
076: */
077: public static final int TRANSACTION_REQUIRED = 2;
078:
079: /**
080: * A reference to the transactions repository of the
081: * <code>TransactionManager</code>.
082: */
083: private Map transactions = null;
084:
085: /**
086: * The type of demarcation for this transaction.
087: * It will remain as required when set to this demarcation type.
088: * Initially, all transaction are supports.
089: */
090: private int demarcation = TRANSACTION_SUPPORTS;
091:
092: /**
093: * The number of extra references to this transaction.
094: */
095: private int references = 0;
096:
097: /**
098: * The number of extra references to this transaction
099: * since a REQUIRED request;
100: */
101: private int sinceRequiredReferences = 0;
102:
103: /**
104: * A <code>Connection</code> for this transaction.
105: */
106: private Connection connection = null;
107:
108: /**
109: * A <code>boolean</code> indicating if this Transaction was yet
110: * rolled back or not.
111: */
112: private boolean rolledBack;
113:
114: /**
115: * The time in milliseconds since this transaction was not used.
116: */
117: private long idleTime;
118:
119: /**
120: * Creates an instance of this class.
121: *
122: * @param transactions a reference to the transactions repository
123: * of the <code>TransactionManager</code>
124: * @param demarcation the initial demarcation for this transaction
125: * @param connection a <code>Connection</code> for this transaction
126: */
127: public Transaction(Map transactions, int demarcation,
128: Connection connection) {
129: this .transactions = transactions;
130: this .demarcation = demarcation;
131: this .connection = connection;
132: this .rolledBack = false;
133: this .references = 0;
134: this .sinceRequiredReferences = 0;
135: this .idleTime = System.currentTimeMillis();
136: }
137:
138: /**
139: * Gets the <code>Connection</code> for this transaction.
140: *
141: * @return the <code>Connection</code> for this transaction
142: */
143: public Connection getConnection() {
144: idleTime = System.currentTimeMillis();
145: return connection;
146: }
147:
148: /**
149: * Gets the time in milliseconds when this transaction was created.
150: *
151: * @return the time in milliseconds when this transaction was created
152: */
153: public long getIdleTime() {
154: return idleTime;
155: }
156:
157: /**
158: * Releases this transaction, that is, it returns it to the
159: * <code>TransactionManager</code> repository .
160: *
161: * @throws InternalErrorException if an error ocurred while manipulating
162: * the connection
163: */
164: public void release() throws InternalErrorException {
165: Object caller = ThreadAttachmentManager.getAttachment();
166: try {
167: if (sinceRequiredReferences == 0) {
168: /* If demarcation is required, commit. */
169: if (this .demarcation == TRANSACTION_REQUIRED) {
170: idleTime = System.currentTimeMillis();
171: connection.commit();
172: connection.setAutoCommit(true);
173: /* Now, demarcation must be supports. (the default). */
174: this .demarcation = TRANSACTION_SUPPORTS;
175: }
176: } else {
177: sinceRequiredReferences--;
178: }
179:
180: if (references == 0) {
181: boolean containsKey;
182: synchronized (transactions) {
183: containsKey = transactions.containsKey(caller);
184: /* If caller in the transactions' Map, clean it. */
185: if (containsKey) {
186: transactions.remove(caller);
187: }
188: }
189:
190: /* If caller in the transactions' Map, clean it. */
191: if (containsKey) {
192: transactions = null;
193: connection.close();
194: connection = null;
195: ThreadAttachmentManager.clearAttachment();
196: }
197: } else {
198: references--;
199: }
200: } catch (SQLException e) {
201: throw new InternalErrorException(e);
202: }
203: }
204:
205: /**
206: * Rolls back this transaction.
207: *
208: * @throws InternalErrorException if an error ocurred while manipulating
209: * the connection
210: */
211: public void rollback() throws InternalErrorException {
212:
213: Object caller = ThreadAttachmentManager.getAttachment();
214:
215: try {
216: /* If still not rolled back. */
217: if (!rolledBack) {
218: if (sinceRequiredReferences == 0) {
219: /* If demarcation is required, commit. */
220: if (this .demarcation == TRANSACTION_REQUIRED) {
221: idleTime = System.currentTimeMillis();
222: connection.rollback();
223: rolledBack = true;
224: connection.setAutoCommit(true);
225: /* Now, demarcation must be supports. (the default). */
226: this .demarcation = TRANSACTION_SUPPORTS;
227: }
228: } else {
229: sinceRequiredReferences--;
230: }
231:
232: if (references == 0) {
233: boolean containsKey;
234: synchronized (transactions) {
235: containsKey = transactions.containsKey(caller);
236: /* If caller in the transactions' Map, clean it. */
237: if (containsKey) {
238: transactions.remove(caller);
239: }
240: }
241:
242: /* If caller in the transactions' Map, clean it. */
243: if (containsKey) {
244: transactions = null;
245: connection.close();
246: connection = null;
247: ThreadAttachmentManager.clearAttachment();
248: }
249: } else {
250: references--;
251: }
252: }
253: } catch (SQLException e) {
254: throw new InternalErrorException(e);
255: }
256: }
257:
258: /**
259: * Cleans this transaction.
260: *
261: * @param transactionKey the <code>Object</code> which identifies
262: * this transaction
263: * @throws InternalErrorException if an error ocurred while manipulating
264: * the connection
265: */
266: void cleanTransaction(Object transactionKey)
267: throws InternalErrorException {
268: try {
269: synchronized (transactions) {
270: transactions.remove(transactionKey);
271: }
272: transactions = null;
273: if (!rolledBack) {
274: connection.rollback();
275: rolledBack = true;
276: }
277:
278: connection.setAutoCommit(true);
279: connection.close();
280: this .demarcation = TRANSACTION_SUPPORTS;
281: } catch (SQLException e) {
282: throw new InternalErrorException(e);
283: } finally {
284: connection = null;
285: }
286: }
287:
288: /**
289: * Join the caller thread to the this transaction.
290: *
291: * @param newDemarcation the demarcation for the joining operation
292: */
293: void join(int newDemarcation) {
294: /* Always increment the references. */
295: this .references++;
296: switch (newDemarcation) {
297: case TRANSACTION_REQUIRED:
298: /*
299: * Increment since required references counter,
300: * when the old demarcation is also required.
301: */
302: if (this .demarcation == TRANSACTION_REQUIRED) {
303: this .sinceRequiredReferences++;
304: }
305: /* And always set the new demarcation. */
306: this .demarcation = newDemarcation;
307: break;
308: case TRANSACTION_SUPPORTS:
309: /*
310: * If the current demarcation is required, increment since
311: * required references counter.
312: */
313: switch (this .demarcation) {
314: case TRANSACTION_REQUIRED:
315: this .sinceRequiredReferences++;
316: break;
317: case TRANSACTION_SUPPORTS:
318: break;
319: default: // Nothing to do
320: } // internal switch
321: /* We never need to set the demarcation: preserve the current.*/
322: break;
323: default: // Nothing to do
324: } // switch
325: }
326:
327: }
|