001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.connector.outbound.transactionlog; 019 020 import java.sql.Connection; 021 import java.sql.PreparedStatement; 022 import java.sql.ResultSet; 023 import java.sql.SQLException; 024 import java.util.ArrayList; 025 import java.util.Collection; 026 import java.util.Iterator; 027 import java.util.List; 028 029 import javax.sql.DataSource; 030 import javax.transaction.xa.Xid; 031 032 import org.apache.geronimo.connector.outbound.ConnectionFactorySource; 033 import org.apache.geronimo.gbean.GBeanLifecycle; 034 import org.apache.geronimo.transaction.manager.LogException; 035 import org.apache.geronimo.transaction.manager.Recovery; 036 import org.apache.geronimo.transaction.manager.TransactionBranchInfo; 037 import org.apache.geronimo.transaction.manager.TransactionBranchInfoImpl; 038 import org.apache.geronimo.transaction.manager.TransactionLog; 039 import org.apache.geronimo.transaction.manager.XidFactory; 040 041 /** 042 * "Last Resource optimization" for single servers wishing to have valid xa transactions with 043 * a single 1-pc datasource. The database is used for the log, and the database work is 044 * committed when the log writes its prepare record. 045 * 046 * @version $Rev: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $ 047 */ 048 public class JDBCLog implements TransactionLog, GBeanLifecycle { 049 private final static String INSERT_XID = "INSERT INTO TXLOG (SYSTEMID, FORMATID, GLOBALID, GLOBALBRANCHID, BRANCHBRANCHID, NAME) VALUES (?, ?, ?, ?, ?)"; 050 private final static String DELETE_XID = "DELETE FROM TXLOG WHERE SYSTEMID = ? AND FORMATID = ? AND GLOBALID = ? AND GLOBALBRANCHID = ?"; 051 private final static String RECOVER = "SELECT FORMATID, GLOBALID, GLOBALBRANCHID, BRANCHBRANCHID, NAME FROM TXLOG WHERE SYSTEMID = ? ORDER BY FORMATID, GLOBALID, GLOBALBRANCHID, BRANCHBRANCHID, NAME"; 052 053 private DataSource dataSource; 054 private final String systemId; 055 private final ConnectionFactorySource managedConnectionFactoryWrapper; 056 057 public JDBCLog(String systemId, ConnectionFactorySource managedConnectionFactoryWrapper) { 058 this.systemId = systemId; 059 this.managedConnectionFactoryWrapper = managedConnectionFactoryWrapper; 060 } 061 062 public JDBCLog(String systemId, DataSource dataSource) { 063 this.systemId = systemId; 064 this.managedConnectionFactoryWrapper = null; 065 this.dataSource = dataSource; 066 } 067 068 public void doStart() throws Exception { 069 dataSource = (DataSource) managedConnectionFactoryWrapper.$getResource(); 070 } 071 072 public void doStop() throws Exception { 073 dataSource = null; 074 } 075 076 public void doFail() { 077 } 078 079 public void begin(Xid xid) throws LogException { 080 } 081 082 public Object prepare(Xid xid, List branches) throws LogException { 083 int formatId = xid.getFormatId(); 084 byte[] globalTransactionId = xid.getGlobalTransactionId(); 085 byte[] branchQualifier = xid.getBranchQualifier(); 086 try { 087 Connection connection = dataSource.getConnection(); 088 try { 089 PreparedStatement ps = connection.prepareStatement(INSERT_XID); 090 try { 091 for (Iterator iterator = branches.iterator(); iterator.hasNext();) { 092 TransactionBranchInfo branch = (TransactionBranchInfo) iterator.next(); 093 ps.setString(0, systemId); 094 ps.setInt(1, formatId); 095 ps.setBytes(2, globalTransactionId); 096 ps.setBytes(3, branchQualifier); 097 ps.setBytes(4, branch.getBranchXid().getBranchQualifier()); 098 ps.setString(5, branch.getResourceName()); 099 ps.execute(); 100 } 101 } finally { 102 ps.close(); 103 } 104 if (!connection.getAutoCommit()) { 105 connection.commit(); 106 } 107 } finally { 108 connection.close(); 109 } 110 } catch (SQLException e) { 111 throw new LogException("Failure during prepare or commit", e); 112 } 113 return null; 114 } 115 116 public void commit(Xid xid, Object logMark) throws LogException { 117 try { 118 Connection connection = dataSource.getConnection(); 119 try { 120 PreparedStatement ps = connection.prepareStatement(DELETE_XID); 121 try { 122 ps.setString(0, systemId); 123 ps.setInt(1, xid.getFormatId()); 124 ps.setBytes(2, xid.getGlobalTransactionId()); 125 ps.setBytes(3, xid.getBranchQualifier()); 126 ps.execute(); 127 } finally { 128 ps.close(); 129 } 130 if (!connection.getAutoCommit()) { 131 connection.commit(); 132 } 133 } finally { 134 connection.close(); 135 } 136 } catch (SQLException e) { 137 throw new LogException("Failure during prepare or commit", e); 138 } 139 } 140 141 public void rollback(Xid xid, Object logMark) throws LogException { 142 throw new LogException("JDBCLog does not support rollback of prepared transactions. Use it only on servers that do not import transactions"); 143 } 144 145 public Collection recover(XidFactory xidFactory) throws LogException { 146 try { 147 Connection connection = dataSource.getConnection(); 148 try { 149 Collection recovered = new ArrayList(); 150 PreparedStatement ps = connection.prepareStatement(RECOVER); 151 try { 152 ps.setString(0, systemId); 153 ResultSet rs = ps.executeQuery(); 154 try { 155 Xid lastXid = null; 156 Xid currentXid = null; 157 Recovery.XidBranchesPair xidBranchesPair = null; 158 while (rs.next()) { 159 int formatId = rs.getInt(0); 160 byte[] globalId = rs.getBytes(1); 161 byte[] globalBranchId = rs.getBytes(2); 162 byte[] branchBranchId = rs.getBytes(3); 163 String name = rs.getString(4); 164 currentXid = xidFactory.recover(formatId, globalId, globalBranchId); 165 Xid branchXid = xidFactory.recover(formatId, globalId, branchBranchId); 166 if (!currentXid.equals(lastXid)) { 167 xidBranchesPair = new Recovery.XidBranchesPair(currentXid, null); 168 recovered.add(xidBranchesPair); 169 lastXid = currentXid; 170 } 171 xidBranchesPair.addBranch(new TransactionBranchInfoImpl(branchXid, name)); 172 } 173 return recovered; 174 } finally { 175 rs.close(); 176 } 177 } finally { 178 ps.close(); 179 } 180 } finally { 181 connection.close(); 182 } 183 } catch (SQLException e) { 184 throw new LogException("Recovery failure", e); 185 } 186 187 } 188 189 public String getXMLStats() { 190 return null; 191 } 192 193 public int getAverageForceTime() { 194 return 0; 195 } 196 197 public int getAverageBytesPerForce() { 198 return 0; 199 } 200 201 }