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 }