blob: 21424483b3788ed6f4ffda9e6a7967efa915d163 [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.openejb.cts;
import javax.sql.ConnectionEventListener;
import javax.sql.StatementEventListener;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
//import javax.sql.StatementEventListener;
public class DriverManagerXADataSource implements XADataSource {
private String jdbcUrl;
private PrintWriter logWriter;
private int loginTimeout;
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(final String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public XAConnection getXAConnection() throws SQLException {
return new DriverManagerXAConnection(jdbcUrl);
}
public XAConnection getXAConnection(final String user, final String password) throws SQLException {
return new DriverManagerXAConnection(jdbcUrl, user, password);
}
public PrintWriter getLogWriter() {
return logWriter;
}
public void setLogWriter(final PrintWriter logWriter) {
this.logWriter = logWriter;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(final int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException("getParentLogger");
}
private static class DriverManagerXAConnection implements XAConnection {
protected Connection connection;
protected LocalXAResource xaResource;
public DriverManagerXAConnection(final String jdbcUrl) throws SQLException {
this(jdbcUrl, null, null);
}
public DriverManagerXAConnection(final String jdbcUrl, final String user, final String password) throws SQLException {
connection = null;
if (user == null) {
connection = DriverManager.getConnection(jdbcUrl);
} else {
connection = DriverManager.getConnection(jdbcUrl, user, password);
}
xaResource = new LocalXAResource(connection);
}
public XAResource getXAResource() {
return xaResource;
}
public Connection getConnection() throws SQLException {
return connection;
}
public void close() throws SQLException {
connection.close();
}
public void addConnectionEventListener(final ConnectionEventListener listener) {
}
public void removeConnectionEventListener(final ConnectionEventListener listener) {
}
public void addStatementEventListener(final StatementEventListener listener) {
}
public void removeStatementEventListener(final StatementEventListener listener) {
}
}
//
// Coppied from commons dbcp
//
protected static class LocalXAResource implements XAResource {
private final Connection connection;
private Xid xid;
private boolean originalAutoCommit;
public LocalXAResource(final Connection localTransaction) {
this.connection = localTransaction;
}
public synchronized Xid getXid() {
return xid;
}
public synchronized void start(final Xid xid, final int flag) throws XAException {
if (flag == XAResource.TMNOFLAGS) {
// first time in this transaction
// make sure we aren't already in another tx
if (this.xid != null) {
throw new XAException("Already enlisted in another transaction with xid " + xid);
}
// save off the current auto commit flag so it can be restored after the transaction completes
try {
originalAutoCommit = connection.getAutoCommit();
} catch (final SQLException ignored) {
// no big deal, just assume it was off
originalAutoCommit = true;
}
// update the auto commit flag
try {
connection.setAutoCommit(false);
} catch (final SQLException e) {
throw (XAException) new XAException("Count not turn off auto commit for a XA transaction").initCause(e);
}
this.xid = xid;
} else if (flag == XAResource.TMRESUME) {
if (xid != this.xid) {
throw new XAException("Attempting to resume in different transaction: expected " + this.xid + ", but was " + xid);
}
} else {
throw new XAException("Unknown start flag " + flag);
}
}
public synchronized void end(final Xid xid, final int flag) throws XAException {
if (xid == null) throw new NullPointerException("xid is null");
if (!this.xid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.xid + ", but was " + xid);
// This notification tells us that the application server is done using this
// connection for the time being. The connection is still associated with an
// open transaction, so we must still wait for the commit or rollback method
}
public synchronized int prepare(final Xid xid) {
// if the connection is read-only, then the resource is read-only
// NOTE: this assumes that the outer proxy throws an exception when application code
// attempts to set this in a transaction
try {
if (connection.isReadOnly()) {
// update the auto commit flag
connection.setAutoCommit(originalAutoCommit);
// tell the transaction manager we are read only
return XAResource.XA_RDONLY;
}
} catch (final SQLException ignored) {
// no big deal
}
// this is a local (one phase) only connectiion, so we can't prepare
return XAResource.XA_OK;
}
public synchronized void commit(final Xid xid, final boolean flag) throws XAException {
if (xid == null) throw new NullPointerException("xid is null");
if (!this.xid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.xid + ", but was " + xid);
try {
// make sure the connection isn't already closed
if (connection.isClosed()) {
throw new XAException("Conection is closed");
}
// A read only connection should not be committed
if (!connection.isReadOnly()) {
connection.commit();
}
} catch (final SQLException e) {
throw (XAException) new XAException().initCause(e);
} finally {
try {
connection.setAutoCommit(originalAutoCommit);
} catch (final SQLException e) {
}
this.xid = null;
}
}
public synchronized void rollback(final Xid xid) throws XAException {
if (xid == null) throw new NullPointerException("xid is null");
if (!this.xid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.xid + ", but was " + xid);
try {
connection.rollback();
} catch (final SQLException e) {
throw (XAException) new XAException().initCause(e);
} finally {
try {
connection.setAutoCommit(originalAutoCommit);
} catch (final SQLException e) {
}
this.xid = null;
}
}
public boolean isSameRM(final XAResource xaResource) {
return this == xaResource;
}
public synchronized void forget(final Xid xid) {
if (xid != null && this.xid.equals(xid)) {
this.xid = null;
}
}
public Xid[] recover(final int flag) {
return new Xid[0];
}
public int getTransactionTimeout() {
return 0;
}
public boolean setTransactionTimeout(final int transactionTimeout) {
return false;
}
}
}