001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.sql; 019 020import java.sql.Connection; 021import java.sql.SQLException; 022import java.util.SortedMap; 023import java.util.concurrent.locks.Lock; 024 025import net.sf.hajdbc.Database; 026import net.sf.hajdbc.DatabaseCluster; 027import net.sf.hajdbc.ExceptionType; 028import net.sf.hajdbc.durability.Durability; 029import net.sf.hajdbc.invocation.InvocationStrategy; 030import net.sf.hajdbc.invocation.Invoker; 031import net.sf.hajdbc.tx.TransactionIdentifierFactory; 032 033/** 034 * @author Paul Ferraro 035 * @param <Z> 036 * @param <D> 037 */ 038public class LocalTransactionContext<Z, D extends Database<Z>> implements TransactionContext<Z, D> 039{ 040 final Durability<Z, D> durability; 041 private final Lock lock; 042 private final TransactionIdentifierFactory<? extends Object> transactionIdFactory; 043 volatile Object transactionId; 044 045 /** 046 * @param cluster 047 */ 048 public LocalTransactionContext(DatabaseCluster<Z, D> cluster) 049 { 050 this.lock = cluster.getLockManager().readLock(null); 051 this.durability = cluster.getDurability(); 052 this.transactionIdFactory = cluster.getTransactionIdentifierFactory(); 053 } 054 055 /** 056 * {@inheritDoc} 057 * @see net.sf.hajdbc.sql.TransactionContext#start(net.sf.hajdbc.invocation.InvocationStrategy, java.sql.Connection) 058 */ 059 @Override 060 public InvocationStrategy start(final InvocationStrategy strategy, final Connection connection) throws SQLException 061 { 062 if (this.transactionId != null) return strategy; 063 064 if (connection.getAutoCommit()) 065 { 066 return new InvocationStrategy() 067 { 068 @Override 069 public <ZZ, DD extends Database<ZZ>, T, R, E extends Exception> SortedMap<DD, R> invoke(ProxyFactory<ZZ, DD, T, E> proxy, Invoker<ZZ, DD, T, R, E> invoker) throws E 070 { 071 LocalTransactionContext.this.lock(); 072 073 try 074 { 075 InvocationStrategy durabilityStrategy = LocalTransactionContext.this.durability.getInvocationStrategy(strategy, Durability.Phase.COMMIT, LocalTransactionContext.this.transactionId); 076 077 return durabilityStrategy.invoke(proxy, invoker); 078 } 079 finally 080 { 081 LocalTransactionContext.this.unlock(); 082 } 083 } 084 }; 085 } 086 087 return new InvocationStrategy() 088 { 089 @Override 090 public <ZZ, DD extends Database<ZZ>, T, R, E extends Exception> SortedMap<DD, R> invoke(ProxyFactory<ZZ, DD, T, E> proxy, Invoker<ZZ, DD, T, R, E> invoker) throws E 091 { 092 LocalTransactionContext.this.lock(); 093 094 try 095 { 096 return strategy.invoke(proxy, invoker); 097 } 098 catch (Throwable e) 099 { 100 throw proxy.getExceptionFactory().createException(e); 101 } 102 finally 103 { 104 LocalTransactionContext.this.unlock(); 105 } 106 } 107 }; 108 } 109 110 /** 111 * {@inheritDoc} 112 * @see net.sf.hajdbc.sql.TransactionContext#start(net.sf.hajdbc.invocation.Invoker, java.sql.Connection) 113 */ 114 @Override 115 public <T, R> Invoker<Z, D, T, R, SQLException> start(final Invoker<Z, D, T, R, SQLException> invoker, Connection connection) throws SQLException 116 { 117 if ((this.transactionId == null) || !connection.getAutoCommit()) return invoker; 118 119 return new Invoker<Z, D, T, R, SQLException>() 120 { 121 @Override 122 public R invoke(D database, T object) throws SQLException 123 { 124 return LocalTransactionContext.this.durability.getInvoker(invoker, Durability.Phase.COMMIT, LocalTransactionContext.this.transactionId, ExceptionType.SQL.<SQLException>getExceptionFactory()).invoke(database, object); 125 } 126 }; 127 } 128 129 /** 130 * {@inheritDoc} 131 * @see net.sf.hajdbc.sql.TransactionContext#end(net.sf.hajdbc.invocation.InvocationStrategy, net.sf.hajdbc.durability.Durability.Phase) 132 */ 133 @Override 134 public InvocationStrategy end(final InvocationStrategy strategy, final Durability.Phase phase) 135 { 136 if (this.transactionId == null) return strategy; 137 138 return new InvocationStrategy() 139 { 140 @Override 141 public <ZZ, DD extends Database<ZZ>, T, R, E extends Exception> SortedMap<DD, R> invoke(ProxyFactory<ZZ, DD, T, E> proxy, Invoker<ZZ, DD, T, R, E> invoker) throws E 142 { 143 InvocationStrategy durabilityStrategy = LocalTransactionContext.this.durability.getInvocationStrategy(strategy, phase, LocalTransactionContext.this.transactionId); 144 145 try 146 { 147 return durabilityStrategy.invoke(proxy, invoker); 148 } 149 finally 150 { 151 LocalTransactionContext.this.unlock(); 152 } 153 } 154 }; 155 } 156 157 /** 158 * {@inheritDoc} 159 * @see net.sf.hajdbc.sql.TransactionContext#end(net.sf.hajdbc.invocation.Invoker, net.sf.hajdbc.durability.Durability.Phase) 160 */ 161 @Override 162 public <T, R> Invoker<Z, D, T, R, SQLException> end(final Invoker<Z, D, T, R, SQLException> invoker, Durability.Phase phase) 163 { 164 if (this.transactionId == null) return invoker; 165 166 return this.durability.getInvoker(invoker, phase, this.transactionId, ExceptionType.SQL.<SQLException>getExceptionFactory()); 167 } 168 169 /** 170 * @see net.sf.hajdbc.sql.TransactionContext#close() 171 */ 172 @Override 173 public void close() 174 { 175 // Tsk, tsk... User neglected to commit/rollback transaction 176 if (this.transactionId != null) 177 { 178 this.unlock(); 179 } 180 } 181 182 void lock() 183 { 184 this.lock.lock(); 185 this.transactionId = this.transactionIdFactory.createTransactionIdentifier(); 186 } 187 188 void unlock() 189 { 190 this.lock.unlock(); 191 this.transactionId = null; 192 } 193}