001/* 002 * JKNIV, whinstone one contract to access your database. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.whinstone.transaction; 021 022import java.util.HashMap; 023import java.util.Map; 024 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import net.sf.jkniv.whinstone.ConnectionAdapter; 029 030/** 031 * Represent a set of transactions for thread, where one thread can use many TransactionContexts. 032 * 033 * Responsible to release (close) the connection with data source. 034 * 035 * @author Alisson Gomes 036 * @since 0.6.0 037 */ 038public class TransactionSessions 039{ 040 private static final Logger LOG = LoggerFactory 041 .getLogger(TransactionSessions.class); 042 private static final ThreadLocal<Map<String, TransactionContext>> RESOURCES = new ThreadLocal<Map<String, TransactionContext>>(); //"Transactional resources" 043 044 public static boolean isEmpty(String contextName) 045 { 046 return (get(contextName) == null); 047 } 048 049 public static TransactionContext get(String contextName) 050 { 051 Map<String, TransactionContext> context = RESOURCES.get(); 052 if (context == null) 053 return null; 054 055 //throw new TransactionException("There is no current transaction session named ["+name+"]"); 056 return context.get(contextName); 057 } 058 059 public static TransactionContext set(String contextName, Transactional tx, ConnectionAdapter conn) 060 { 061 if (tx == null) 062 throw new TransactionException("Cannot set a null connection to transaction context"); 063 064 Map<String, TransactionContext> transactionContext = RESOURCES.get(); 065 if (transactionContext == null) 066 { 067 transactionContext = new HashMap<String, TransactionContext>(); 068 RESOURCES.set(transactionContext); 069 } 070 if (transactionContext.isEmpty() || !transactionContext.containsKey(contextName)) 071 { 072 transactionContext.put(contextName, new TransactionContext(contextName, tx, conn)); 073 } 074 else if (transactionContext.containsKey(contextName)) 075 throw new TransactionException("Already exists a connection bound to context name [" + contextName + "] for this thread. jkniv-whinstone-jdbc doesn't support multiple or nested transactions in same Thread!"); 076 077 return transactionContext.get(contextName); 078 } 079 080 public static void close(String contextName) 081 { 082// ACTIVE | MARKED_ROLLBACK | PREPARED | COMMITED | ROLLBACK | UNKNOW | NO_TRANSACTION | PREPARING | COMMITING | ROLLING_BACK 083// release when: MARKED_ROLLBACK | ROLLING_BACK | ROLLBACK | PREPARED | COMMITED | COMMITING 084 TransactionContext transactionContext = get(contextName); 085 TransactionStatus txStatus =transactionContext.getTransactional().getStatus(); 086 if (txStatus == TransactionStatus.COMMITTED || txStatus == TransactionStatus.ROLLEDBACK) 087 releaseResource(contextName); 088 089 else if (txStatus == TransactionStatus.MARKED_ROLLBACK || txStatus == TransactionStatus.PREPARING) 090 { 091 LOG.warn("Be careful transaction was finished with status [{}]", txStatus); 092 releaseResource(contextName); 093 } 094 else// ACTIVE? PREPARED? UNKNOWN? NO_TRANSACTION? ? COMMITTING ? ROLLING_BACK? 095 { 096 LOG.error("Transaction was closed with status [{}]", txStatus); 097 releaseResource(contextName); 098 // FIXME transaction design, what's happens with status: ACTIVE? PREPARED? UNKNOWN? NO_TRANSACTION? ? COMMITTING ? ROLLING_BACK? 099 //throw new TransactionException(" exists a connection bind to context name [" + name + "] for this thread!"); 100 } 101 //setTransactionStatus(TransactionStatus.NO_TRANSACTION); 102 } 103 104 private static void releaseResource(String contextName) 105 { 106 try 107 { 108 TransactionContext txContext = get(contextName); 109 txContext.getConnection().close(); 110 Map<String, TransactionContext> contexts = RESOURCES.get(); 111 if (contexts.size() == 1) // 112 { 113 contexts.clear(); 114 RESOURCES.remove(); 115 } 116 else 117 { 118 contexts.remove(contextName); 119 } 120 } 121 finally 122 { 123 RESOURCES.remove(); 124 } 125 } 126}