001: /*
002: * $Header: /export/home/cvsroot/MyPersonalizerRepository/MyPersonalizer/Subsystems/Kernel/Sources/es/udc/mypersonalizer/kernel/model/repository/sql/plain/TransactionManager.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.sql.Connection;
044: import java.sql.SQLException;
045: import java.util.HashMap;
046: import java.util.Iterator;
047: import java.util.Map;
048:
049: import es.udc.mypersonalizer.kernel.config.KernelConfig;
050: import es.udc.mypersonalizer.kernel.config.KernelConfigManager;
051: import es.udc.mypersonalizer.kernel.config.TransactionManagerConfig;
052: import es.udc.mypersonalizer.kernel.log.Log;
053: import es.udc.mypersonalizer.kernel.log.LogManager;
054: import es.udc.mypersonalizer.kernel.log.LogNamingConventions;
055: import es.udc.mypersonalizer.kernel.util.exceptions.InternalErrorException;
056:
057: /** This concrete singleton class provides a manager to work with
058: * transaction. It uses a connection Pool in order to efficiently
059: * manage database connections. For this task it uses the
060: * <code>ConnectionPoolAdapterSingleton</code>.
061: * <p>
062: * Each <code>Transaction</code> is identified by an <code>Object</code>
063: * that is attached to the thread managing the transaction. This class
064: * is responsible for getting (and creating when needed) this Object.
065: * <p>
066: * It employs a transaction timeout mechanism (a thread) to collect
067: * garbage transactions, and therefore, connections. This does not
068: * assures that good transactions be collected: you will need to
069: * increase your configuration transaction timeout.<p>
070: *
071: * This Transaction Manager gets its configuration information from
072: * {@link TransactionManagerConfig}.
073: *
074: * @author Fernando Bellas
075: * @author Daniel Fernandez
076: * @since 1.0
077: */
078: public final class TransactionManager {
079:
080: /**
081: * The singleton instance.
082: */
083: private static TransactionManager singleton;
084:
085: /**
086: * The <code>Transaction</code<s repository.
087: */
088: private static Map transactions = null;
089:
090: /**
091: * The connection pool.
092: */
093: private static ConnectionPoolAdapterSingleton connectionPool = null;
094:
095: /*
096: * Initialises the singleton instance by reading the configuration
097: * information
098: */
099: static {
100:
101: try {
102: KernelConfig kernelConfig = KernelConfigManager.getConfig();
103:
104: TransactionManagerConfig transactionManagerConfig = kernelConfig
105: .getKernelModelConfig()
106: .getTransactionManagerConfig();
107:
108: String transactionCleanerActiveStr = transactionManagerConfig
109: .getTransactionCleanerActive();
110: String transactionCleaningTimeOutStr = transactionManagerConfig
111: .getTransactionCleaningTimeOutSeconds();
112: String transactionTimeOutSecondsStr = transactionManagerConfig
113: .getTransactionTimeOutSeconds();
114:
115: boolean transactionCleanerActive = (new Boolean(
116: transactionCleanerActiveStr)).booleanValue();
117: int transactionCleaningTimeOut = -1;
118: int transactionTimeOutSeconds = -1;
119:
120: /* Initialises the log */
121: Log mypersonalizerLog = LogManager
122: .getLog(LogNamingConventions.MYPERSONALIZER);
123:
124: transactions = new HashMap();
125:
126: if (transactionCleanerActive) {
127:
128: /*
129: * If the transaction cleaner thread is to be active,
130: * initialises and runs it.
131: */
132: transactionCleaningTimeOut = (new Integer(
133: transactionCleaningTimeOutStr)).intValue();
134: transactionTimeOutSeconds = (new Integer(
135: transactionTimeOutSecondsStr)).intValue();
136:
137: TransactionCleaner transactionCleaner = new TransactionCleaner(
138: transactionCleaningTimeOut,
139: transactionTimeOutSeconds, transactions,
140: mypersonalizerLog);
141:
142: transactionCleaner.start();
143:
144: }
145:
146: connectionPool = ConnectionPoolAdapterSingleton
147: .getInstance();
148:
149: singleton = new TransactionManager();
150:
151: /* We log the starting of the Transaction Manager */
152: mypersonalizerLog.write(
153: "TransactionManager started (Cleaner: "
154: + (transactionCleanerActive ? "active"
155: : "inactive")
156: + " , Cleaning period: "
157: + transactionCleaningTimeOut
158: + " seconds, TransactionsTimeOut: "
159: + transactionTimeOutSeconds + " seconds).",
160: null, // no exception
161: TransactionManager.class);
162:
163: } catch (Exception e) {
164:
165: Log mypersonalizerLog = LogManager
166: .getLog(LogNamingConventions.MYPERSONALIZER);
167: mypersonalizerLog.write(
168: "Could not initialize configuration for "
169: + "TransactionManager", e,
170: TransactionManager.class);
171:
172: }
173:
174: }
175:
176: /** Disallows creating instances of this class. */
177: protected TransactionManager() {
178: }
179:
180: /**
181: * Retuns the singleton.
182: *
183: * @return the singleton
184: */
185: public static TransactionManager getInstance() {
186: return singleton;
187: }
188:
189: /**
190: * Retuns a <code>Transaction</code>, with the default isolation level.
191: *
192: * @param required indicates the transaction demarcation.
193: * @return a <code>Transaction</code>
194: * @throws InternalErrorException if an error ocurred while manipulating
195: * <code<Connection</code>s or the pool.
196: */
197: public Transaction getTransaction(int required)
198: throws InternalErrorException {
199:
200: return getTransaction(required, null);
201: }
202:
203: /**
204: * Retuns a <code>Transaction</code>, with the specified isolation
205: * level, which only means if a new transaction is created. Otherwise,
206: * if the transaction already exists, this level will not be taken in
207: * account.
208: *
209: * @param required indicates the transaction demarcation.
210: * @param isolationLevel the transaction isolation level.
211: * @return a <code>Transaction</code>
212: * @throws InternalErrorException if an error ocurred while manipulating
213: * <code<Connection</code>s or the pool.
214: */
215: public Transaction getTransaction(int required, int isolationLevel)
216: throws InternalErrorException {
217:
218: return getTransaction(required, new Integer(isolationLevel));
219: }
220:
221: /**
222: * Retuns a <code>Transaction</code>.
223: *
224: * @param required indicates the transaction demarcation.
225: * @param isolationLevel the transaction isolation level. May be a null
226: * value indicating default isolation level.
227: * @return a <code>Transaction</code>
228: * @throws InternalErrorException if an error ocurred while manipulating
229: * <code<Connection</code>s or the pool.
230: */
231: private Transaction getTransaction(int required,
232: Integer isolationLevel) throws InternalErrorException {
233:
234: Object caller = null;
235: Transaction transaction = null;
236:
237: /* Get the object attached to the current thread. */
238: Object threadAttachment = ThreadAttachmentManager
239: .getAttachment();
240:
241: /* If there is nothing, create it. */
242: if (threadAttachment == null) {
243: caller = new Object();
244: ThreadAttachmentManager.setAttachment(caller);
245: } else {
246: /* Otherwise, use the attached object. */
247: caller = threadAttachment;
248: }
249:
250: /* Try to find a transaction. */
251: synchronized (transactions) {
252: transaction = (Transaction) transactions.get(caller);
253: }
254:
255: /* If not found... */
256: try {
257: if (transaction == null) {
258: /* Gets a Connection. */
259: Connection connection = connectionPool.getConnection();
260: /* Sets the transaction isolation, if specified. */
261: if (isolationLevel != null) {
262: connection.setTransactionIsolation(isolationLevel
263: .intValue());
264: }
265: /* Creates a Transaction, depending on the demarcation. */
266: switch (required) {
267: case Transaction.TRANSACTION_REQUIRED:
268: connection.setAutoCommit(false);
269: /* Don't break, we also need to create the transaction. */
270:
271: case Transaction.TRANSACTION_SUPPORTS:
272: transaction = new Transaction(transactions,
273: required, connection);
274: connection = null;
275: break; // All done
276:
277: default:
278: /* Unknown demarcation. */
279: throw new InternalErrorException(
280: "Unknown transaction demarcation: "
281: + required);
282: }
283:
284: /* Add the transaction to the repository. */
285: synchronized (transactions) {
286: transactions.put(caller, transaction);
287: }
288:
289: } else { // If found...
290: /* Join the transaction with the corresponding demarcation. */
291: switch (required) {
292: case Transaction.TRANSACTION_REQUIRED:
293: Connection connection = transaction.getConnection();
294: connection.setAutoCommit(false);
295: connection = null;
296: /* Don't break, we also need to join the transaction. */
297:
298: case Transaction.TRANSACTION_SUPPORTS:
299: transaction.join(required);
300: break; // All done
301:
302: default:
303: /* Unknown demarcation. */
304: throw new InternalErrorException(
305: "Unknown transaction demarcation: "
306: + required);
307: } // switch
308: }
309: } catch (SQLException e) {
310: throw new InternalErrorException(e);
311: }
312: return transaction;
313: }
314:
315: /**
316: * The thread that cleans the transaction repository.
317: */
318: private static class TransactionCleaner extends Thread {
319:
320: private int transactionCleaningPeriodSeconds;
321: private long transactionTimeOutSeconds;
322: private Map transactions;
323: private Log log;
324:
325: public TransactionCleaner(int transactionCleaningPeriodSeconds,
326: long transactionTimeOutSeconds, Map transactions,
327: Log log) {
328:
329: this .transactionCleaningPeriodSeconds = transactionCleaningPeriodSeconds;
330: this .transactionTimeOutSeconds = transactionTimeOutSeconds;
331: this .transactions = transactions;
332: this .log = log;
333: }
334:
335: public void run() {
336:
337: while (true) {
338:
339: try {
340:
341: sleep(transactionCleaningPeriodSeconds * 1000);
342:
343: /* Gets the current time. */
344: long currentTime = System.currentTimeMillis();
345:
346: /* Traverse the transactions repository... */
347:
348: /*
349: * We need to synchronize to do more than one operation
350: * over the Map.
351: */
352: synchronized (transactions) {
353:
354: /* Gets the Transactions. */
355: Iterator iterator = transactions.keySet()
356: .iterator();
357:
358: /* Iterates over them. */
359: while (iterator.hasNext()) {
360:
361: /* Get the transaction Key. */
362: Object transactionKey = iterator.next();
363:
364: /* Gets the transaction. */
365: Transaction transaction = (Transaction) transactions
366: .get(transactionKey);
367:
368: /* Gets the idle time. */
369: long idleTime = transaction.getIdleTime();
370:
371: /* Compute the difference time... */
372: long difference = currentTime - idleTime;
373:
374: /* Check if the timeout was expired. */
375: if (difference >= transactionTimeOutSeconds * 1000) {
376:
377: log.write("Cleaning Transaction: "
378: + transaction, null,
379: TransactionCleaner.class);
380:
381: /* If expired, clean the Transaction... */
382: transaction
383: .cleanTransaction(transactionKey);
384:
385: }
386: }
387: }
388:
389: } catch (Exception exception) {
390:
391: log
392: .write(
393: "The transactions cleaner got an exception.",
394: exception, TransactionCleaner.class);
395: }
396: }
397: }
398: }
399:
400: }
|