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 }