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.jdbc.transaction; 021 022import java.sql.SQLException; 023 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027import net.sf.jkniv.asserts.Assertable; 028import net.sf.jkniv.asserts.AssertsFactory; 029import net.sf.jkniv.whinstone.ConnectionAdapter; 030import net.sf.jkniv.whinstone.transaction.TransactionContext; 031import net.sf.jkniv.whinstone.transaction.TransactionException; 032import net.sf.jkniv.whinstone.transaction.TransactionScope; 033import net.sf.jkniv.whinstone.transaction.TransactionSessions; 034import net.sf.jkniv.whinstone.transaction.TransactionStatus; 035import net.sf.jkniv.whinstone.transaction.Transactional; 036 037public abstract class AbstractTransaction implements Transactional 038{ 039 protected static final Logger logger = LoggerFactory.getLogger(AbstractTransaction.class); 040 private static final Assertable NOT_NULL = AssertsFactory.getNotNull(); 041 private boolean wasAutoCommit; 042 private TransactionStatus status; 043 protected final TransactionScope transactionScope; 044 protected final String contextName; 045 protected final ConnectionAdapter connAdapter; 046 047 /** 048 * Open new connection with default connection properties from driver version. 049 * @return new Connection 050 */ 051 public abstract ConnectionAdapter open(); 052 053 public AbstractTransaction(String contextName, ConnectionAdapter connAdapter, TransactionScope transactionScope) 054 { 055 NOT_NULL.verify(contextName, connAdapter, transactionScope); 056 this.contextName = contextName; 057 this.connAdapter = connAdapter; 058 this.status = TransactionStatus.NO_TRANSACTION; 059 this.transactionScope = transactionScope;// FIXME tx implements different scopes transactions 060 try 061 { 062 this.wasAutoCommit = connAdapter.isAutoCommit(); 063 } 064 catch (SQLException e) 065 { 066 throw new TransactionException("Could not create transaction for " + toString(), e); 067 } 068 } 069 070 @Override 071 public final void begin() //throws NotSupportedException, SystemException 072 { 073 if (logger.isTraceEnabled()) 074 logger.trace("Creating new transaction [{}] with scope [{}]", this.contextName, transactionScope); 075 TransactionContext transactionContext = getTransactionContext(); 076 077 if (transactionContext.isActive()) 078 throw new TransactionException( 079 "Transaction is active. Local transaction does not support nested transactions"); 080 081 this.status = TransactionStatus.PREPARING; 082 //ConnectionAdapter conn = transactionContext.getConnection(); 083 try 084 { 085 //this.wasAutoCommit = conn.isAutoCommit(); 086 if (wasAutoCommit) 087 { 088 connAdapter.autoCommitOff(); 089 this.status = TransactionStatus.ACTIVE; 090 if (logger.isDebugEnabled()) 091 logger.debug("Transaction [{}] with scope [{}] ACTIVE", this.contextName, transactionScope); 092 } 093 else 094 this.status = TransactionStatus.ACTIVE; 095 } 096 catch (SQLException sqle) 097 { 098 //TransactionContext.setTransactionStatus(TransactionStatus.); 099 throw new TransactionException("Cannot begin transaction", sqle); 100 } 101 finally 102 { 103 104 } 105 } 106 107 @Override 108 public final void commit() //throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException 109 { 110 if (logger.isTraceEnabled()) 111 logger.trace("Committing transaction [{}] with scope [{}]", this.contextName, transactionScope); 112 113 TransactionContext transactionContext = getTransactionContext(); 114 //transactionContext.setStatus(TransactionStatus.COMMITTING); 115 //ConnectionAdapter conn = transactionContext.getConnection(); 116 try 117 { 118 if (!transactionContext.isActive()) 119 throw new TransactionException("Does not have transaction beginning. Autocommit is true!"); 120 121 connAdapter.commit(); 122 this.status = TransactionStatus.COMMITTED; 123 if (logger.isDebugEnabled()) 124 logger.debug("Transaction [{}] with scope [{}] COMMITTED", this.contextName, transactionScope); 125 126 } 127 catch (SQLException sqle) 128 { 129 this.status = TransactionStatus.MARKED_ROLLBACK; 130 throw new TransactionException("Cannot commit transaction", sqle); 131 } 132 finally 133 { 134 try 135 { 136 if (wasAutoCommit) 137 connAdapter.autoCommitOn(); 138 } 139 catch (SQLException sqle) 140 { 141 logger.warn("Cannot change connection to autocommit=true. Reason: " + sqle.getMessage()); 142 } 143 TransactionSessions.close(this.contextName); 144 } 145 } 146 147 @Override 148 public final TransactionStatus getStatus() //throws SystemException 149 { 150 return this.status; 151 //return getTransactionContext().getStatus(); 152 } 153 154 @Override 155 public final void rollback() 156 { 157 if (logger.isTraceEnabled()) 158 logger.trace("Rolling back transaction [{}] with scope [{}]", this.contextName, transactionScope); 159 160 TransactionContext transactionContext = getTransactionContext(); 161 //ConnectionAdapter conn = transactionContext.getConnection(); 162// if (conn == null) 163// { 164// logger.warn("Try to rollback transaction at context [{}] without connection!", this.contextName); 165// return; 166// } 167 try 168 { 169 connAdapter.rollback(); 170 this.status = TransactionStatus.ROLLEDBACK; 171 if (logger.isDebugEnabled()) 172 logger.debug("Transaction [{}] with scope [{}] ROLLBACK", this.contextName, transactionScope); 173 } 174 catch (SQLException sqle) 175 { 176 this.status = TransactionStatus.MARKED_ROLLBACK; 177 logger.warn("Fail to try rollback transaction. Reason: " + sqle.getLocalizedMessage()); 178 throw new TransactionException("Cannot rollback transaction", sqle); 179 } 180 finally 181 { 182 try 183 { 184 if (wasAutoCommit && !connAdapter.isAutoCommit()) 185 connAdapter.autoCommitOn(); 186 } 187 catch (SQLException sqle) 188 { 189 logger.warn("Cannot back auto-commit connection to TRUE. Reason: " + sqle.getMessage()); 190 } 191 TransactionSessions.close(this.contextName); 192 } 193 } 194 195 private TransactionContext getTransactionContext() 196 { 197 TransactionContext transactionContext = TransactionSessions.get(this.contextName); 198 if (transactionContext == null) 199 { 200 //ConnectionAdapter conn = open(); 201 transactionContext = TransactionSessions.set(this.contextName, this, connAdapter); 202 } 203 return transactionContext; 204 } 205 206 207 @Override 208 public String toString() 209 { 210 return "AbstractTransaction [contextName=" + contextName + ", status=" + status + ", transactionScope=" 211 + transactionScope + ", connAdapter=" + connAdapter + "]"; 212 } 213 214 215 // @Override 216 // public final void setTransactionTimeout(int seconds) throws TransactionException 217 // { 218 // } 219 220 /* 221 public Transaction getTransaction() //throws SystemException 222 { 223 224 return null; 225 } 226 227 public void resume(Transaction transaction)// throws InvalidTransactionException, IllegalStateException, SystemException 228 { 229 230 } 231 232 public void setRollbackOnly() //throws IllegalStateException, SystemException 233 { 234 235 } 236 237 public void setTransactionTimeout(int timeout) //throws SystemException 238 { 239 240 } 241 242 public Transaction suspend() //throws SystemException 243 { 244 return null; 245 } 246 */ 247 248}