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