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.resource.ResourceException; 030 import javax.sql.DataSource; 031 import javax.transaction.xa.Xid; 032 033 import org.apache.geronimo.gbean.GBeanLifecycle; 034 import org.apache.geronimo.naming.ResourceSource; 035 import org.apache.geronimo.transaction.manager.LogException; 036 import org.apache.geronimo.transaction.manager.Recovery; 037 import org.apache.geronimo.transaction.manager.TransactionBranchInfo; 038 import org.apache.geronimo.transaction.manager.TransactionBranchInfoImpl; 039 import org.apache.geronimo.transaction.manager.TransactionLog; 040 import org.apache.geronimo.transaction.manager.XidFactory; 041 042 /** 043 * "Last Resource optimization" for single servers wishing to have valid xa transactions with 044 * a single 1-pc datasource. The database is used for the log, and the database work is 045 * committed when the log writes its prepare record. 046 * 047 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 048 */ 049 public class JDBCLog implements TransactionLog, GBeanLifecycle { 050 private final static String INSERT_XID = "INSERT INTO TXLOG (SYSTEMID, FORMATID, GLOBALID, GLOBALBRANCHID, BRANCHBRANCHID, NAME) VALUES (?, ?, ?, ?, ?)"; 051 private final static String DELETE_XID = "DELETE FROM TXLOG WHERE SYSTEMID = ? AND FORMATID = ? AND GLOBALID = ? AND GLOBALBRANCHID = ?"; 052 private final static String RECOVER = "SELECT FORMATID, GLOBALID, GLOBALBRANCHID, BRANCHBRANCHID, NAME FROM TXLOG WHERE SYSTEMID = ? ORDER BY FORMATID, GLOBALID, GLOBALBRANCHID, BRANCHBRANCHID, NAME"; 053 054 private DataSource dataSource; 055 private final String systemId; 056 private final ResourceSource<ResourceException> managedConnectionFactoryWrapper; 057 058 public JDBCLog(String systemId, ResourceSource<ResourceException> managedConnectionFactoryWrapper) { 059 this.systemId = systemId; 060 this.managedConnectionFactoryWrapper = managedConnectionFactoryWrapper; 061 } 062 063 public JDBCLog(String systemId, DataSource dataSource) { 064 this.systemId = systemId; 065 this.managedConnectionFactoryWrapper = null; 066 this.dataSource = dataSource; 067 } 068 069 public void doStart() throws Exception { 070 dataSource = (DataSource) managedConnectionFactoryWrapper.$getResource(); 071 } 072 073 public void doStop() throws Exception { 074 dataSource = null; 075 } 076 077 public void doFail() { 078 } 079 080 public void begin(Xid xid) throws LogException { 081 } 082 083 public Object prepare(Xid xid, List branches) throws LogException { 084 int formatId = xid.getFormatId(); 085 byte[] globalTransactionId = xid.getGlobalTransactionId(); 086 byte[] branchQualifier = xid.getBranchQualifier(); 087 try { 088 Connection connection = dataSource.getConnection(); 089 try { 090 PreparedStatement ps = connection.prepareStatement(INSERT_XID); 091 try { 092 for (Iterator iterator = branches.iterator(); iterator.hasNext();) { 093 TransactionBranchInfo branch = (TransactionBranchInfo) iterator.next(); 094 ps.setString(0, systemId); 095 ps.setInt(1, formatId); 096 ps.setBytes(2, globalTransactionId); 097 ps.setBytes(3, branchQualifier); 098 ps.setBytes(4, branch.getBranchXid().getBranchQualifier()); 099 ps.setString(5, branch.getResourceName()); 100 ps.execute(); 101 } 102 } finally { 103 ps.close(); 104 } 105 if (!connection.getAutoCommit()) { 106 connection.commit(); 107 } 108 } finally { 109 connection.close(); 110 } 111 } catch (SQLException e) { 112 throw new LogException("Failure during prepare or commit", e); 113 } 114 return null; 115 } 116 117 public void commit(Xid xid, Object logMark) throws LogException { 118 try { 119 Connection connection = dataSource.getConnection(); 120 try { 121 PreparedStatement ps = connection.prepareStatement(DELETE_XID); 122 try { 123 ps.setString(0, systemId); 124 ps.setInt(1, xid.getFormatId()); 125 ps.setBytes(2, xid.getGlobalTransactionId()); 126 ps.setBytes(3, xid.getBranchQualifier()); 127 ps.execute(); 128 } finally { 129 ps.close(); 130 } 131 if (!connection.getAutoCommit()) { 132 connection.commit(); 133 } 134 } finally { 135 connection.close(); 136 } 137 } catch (SQLException e) { 138 throw new LogException("Failure during prepare or commit", e); 139 } 140 } 141 142 public void rollback(Xid xid, Object logMark) throws LogException { 143 throw new LogException("JDBCLog does not support rollback of prepared transactions. Use it only on servers that do not import transactions"); 144 } 145 146 public Collection recover(XidFactory xidFactory) throws LogException { 147 try { 148 Connection connection = dataSource.getConnection(); 149 try { 150 Collection recovered = new ArrayList(); 151 PreparedStatement ps = connection.prepareStatement(RECOVER); 152 try { 153 ps.setString(0, systemId); 154 ResultSet rs = ps.executeQuery(); 155 try { 156 Xid lastXid = null; 157 Xid currentXid = null; 158 Recovery.XidBranchesPair xidBranchesPair = null; 159 while (rs.next()) { 160 int formatId = rs.getInt(0); 161 byte[] globalId = rs.getBytes(1); 162 byte[] globalBranchId = rs.getBytes(2); 163 byte[] branchBranchId = rs.getBytes(3); 164 String name = rs.getString(4); 165 currentXid = xidFactory.recover(formatId, globalId, globalBranchId); 166 Xid branchXid = xidFactory.recover(formatId, globalId, branchBranchId); 167 if (!currentXid.equals(lastXid)) { 168 xidBranchesPair = new Recovery.XidBranchesPair(currentXid, null); 169 recovered.add(xidBranchesPair); 170 lastXid = currentXid; 171 } 172 xidBranchesPair.addBranch(new TransactionBranchInfoImpl(branchXid, name)); 173 } 174 return recovered; 175 } finally { 176 rs.close(); 177 } 178 } finally { 179 ps.close(); 180 } 181 } finally { 182 connection.close(); 183 } 184 } catch (SQLException e) { 185 throw new LogException("Recovery failure", e); 186 } 187 188 } 189 190 public String getXMLStats() { 191 return null; 192 } 193 194 public int getAverageForceTime() { 195 return 0; 196 } 197 198 public int getAverageBytesPerForce() { 199 return 0; 200 } 201 202 }