Skip to content

Commit

Permalink
refactor: state of PGXAConnection as enum (#966)
Browse files Browse the repository at this point in the history
make state of PGXAConnection as enum
  • Loading branch information
AlexElin authored and davecramer committed Oct 24, 2017
1 parent bdfc1db commit 7618822
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 36 deletions.
Expand Up @@ -62,13 +62,15 @@ public PGPooledConnection(Connection con, boolean autoCommit) {
/**
* Adds a listener for close or fatal error events on the connection handed out to a client.
*/
@Override
public void addConnectionEventListener(ConnectionEventListener connectionEventListener) {
listeners.add(connectionEventListener);
}

/**
* Removes a listener for close or fatal error events on the connection handed out to a client.
*/
@Override
public void removeConnectionEventListener(ConnectionEventListener connectionEventListener) {
listeners.remove(connectionEventListener);
}
Expand All @@ -77,6 +79,7 @@ public void removeConnectionEventListener(ConnectionEventListener connectionEven
* Closes the physical database connection represented by this PooledConnection. If any client has
* a connection based on this PooledConnection, it is forcibly closed as well.
*/
@Override
public void close() throws SQLException {
if (last != null) {
last.close();
Expand Down Expand Up @@ -107,6 +110,7 @@ public void close() throws SQLException {
* called, the previous one is forcibly closed and its work rolled back.
* </p>
*/
@Override
public Connection getConnection() throws SQLException {
if (con == null) {
// Before throwing the exception, let's notify the registered listeners about the error
Expand Down Expand Up @@ -432,9 +436,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
}
}

@Override
public void removeStatementEventListener(StatementEventListener listener) {
}

@Override
public void addStatementEventListener(StatementEventListener listener) {
}

Expand Down
87 changes: 51 additions & 36 deletions pgjdbc/src/main/java/org/postgresql/xa/PGXAConnection.java
Expand Up @@ -50,29 +50,9 @@ public class PGXAConnection extends PGPooledConnection implements XAConnection,
*/
private final BaseConnection conn;

/*
* PGXAConnection-object can be in one of three states:
*
* IDLE Not associated with a XA-transaction. You can still call getConnection and use the
* connection outside XA. currentXid is null. autoCommit is true on a connection by getConnection,
* per normal JDBC rules, though the caller can change it to false and manage transactions itself
* using Connection.commit and rollback.
*
* ACTIVE start has been called, and we're associated with an XA transaction. currentXid is valid.
* autoCommit is false on a connection returned by getConnection, and should not be messed with by
* the caller or the XA transaction will be broken.
*
* ENDED end has been called, but the transaction has not yet been prepared. currentXid is still
* valid. You shouldn't use the connection for anything else than issuing a XAResource.commit or
* rollback.
*/
private Xid currentXid;

private int state;

private static final int STATE_IDLE = 0;
private static final int STATE_ACTIVE = 1;
private static final int STATE_ENDED = 2;
private State state;

/*
* When an XA transaction is started, we put the underlying connection into non-autocommit mode.
Expand All @@ -90,20 +70,21 @@ private void debug(String s) {
public PGXAConnection(BaseConnection conn) throws SQLException {
super(conn, true, true);
this.conn = conn;
this.state = STATE_IDLE;
this.state = State.IDLE;
}

/**
* XAConnection interface
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = super.getConnection();

// When we're outside an XA transaction, autocommit
// is supposed to be true, per usual JDBC convention.
// When an XA transaction is in progress, it should be
// false.
if (state == STATE_IDLE) {
if (state == State.IDLE) {
conn.setAutoCommit(true);
}

Expand All @@ -116,6 +97,7 @@ public Connection getConnection() throws SQLException {
new Class[]{Connection.class, PGConnection.class}, handler);
}

@Override
public XAResource getXAResource() {
return this;
}
Expand All @@ -133,7 +115,7 @@ private class ConnectionHandler implements InvocationHandler {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (state != STATE_IDLE) {
if (state != State.IDLE) {
String methodName = method.getName();
if (methodName.equals("commit")
|| methodName.equals("rollback")
Expand Down Expand Up @@ -181,6 +163,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
*
* Postconditions: 1. Connection is associated with the transaction
*/
@Override
public void start(Xid xid, int flags) throws XAException {
if (LOGGER.isLoggable(Level.FINEST)) {
debug("starting transaction xid = " + xid);
Expand All @@ -196,7 +179,7 @@ public void start(Xid xid, int flags) throws XAException {
throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL);
}

if (state == STATE_ACTIVE) {
if (state == State.ACTIVE) {
throw new PGXAException(GT.tr("Connection is busy with another transaction"),
XAException.XAER_PROTO);
}
Expand All @@ -211,7 +194,7 @@ public void start(Xid xid, int flags) throws XAException {

// It's ok to join an ended transaction. WebLogic does that.
if (flags == TMJOIN) {
if (state != STATE_ENDED) {
if (state != State.ENDED) {
throw new PGXAException(GT.tr("Transaction interleaving not implemented"),
XAException.XAER_RMERR);
}
Expand All @@ -220,7 +203,7 @@ public void start(Xid xid, int flags) throws XAException {
throw new PGXAException(GT.tr("Transaction interleaving not implemented"),
XAException.XAER_RMERR);
}
} else if (state == STATE_ENDED) {
} else if (state == State.ENDED) {
throw new PGXAException(GT.tr("Transaction interleaving not implemented"),
XAException.XAER_RMERR);
}
Expand All @@ -237,7 +220,7 @@ public void start(Xid xid, int flags) throws XAException {
}

// Preconditions are met, Associate connection with the transaction
state = STATE_ACTIVE;
state = State.ACTIVE;
currentXid = xid;
}

Expand All @@ -249,6 +232,7 @@ public void start(Xid xid, int flags) throws XAException {
*
* Postconditions: 1. connection is disassociated from the transaction.
*/
@Override
public void end(Xid xid, int flags) throws XAException {
if (LOGGER.isLoggable(Level.FINEST)) {
debug("ending transaction xid = " + xid);
Expand All @@ -265,7 +249,7 @@ public void end(Xid xid, int flags) throws XAException {
throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL);
}

if (state != STATE_ACTIVE || !currentXid.equals(xid)) {
if (state != State.ACTIVE || !currentXid.equals(xid)) {
throw new PGXAException(GT.tr("tried to call end without corresponding start call"),
XAException.XAER_PROTO);
}
Expand All @@ -279,7 +263,7 @@ public void end(Xid xid, int flags) throws XAException {
// if TMFAIL was given.

// All clear. We don't have any real work to do.
state = STATE_ENDED;
state = State.ENDED;
}

/**
Expand All @@ -289,6 +273,7 @@ public void end(Xid xid, int flags) throws XAException {
*
* Postconditions: 1. Transaction is prepared
*/
@Override
public int prepare(Xid xid) throws XAException {
if (LOGGER.isLoggable(Level.FINEST)) {
debug("preparing transaction xid = " + xid);
Expand All @@ -301,11 +286,11 @@ public int prepare(Xid xid) throws XAException {
"Not implemented: Prepare must be issued using the same connection that started the transaction"),
XAException.XAER_RMERR);
}
if (state != STATE_ENDED) {
if (state != State.ENDED) {
throw new PGXAException(GT.tr("Prepare called before end"), XAException.XAER_INVAL);
}

state = STATE_IDLE;
state = State.IDLE;
currentXid = null;

try {
Expand All @@ -332,6 +317,7 @@ public int prepare(Xid xid) throws XAException {
*
* Postconditions: 1. list of prepared xids is returned
*/
@Override
public Xid[] recover(int flag) throws XAException {
// Check preconditions
if (flag != TMSTARTRSCAN && flag != TMENDRSCAN && flag != TMNOFLAGS
Expand Down Expand Up @@ -384,6 +370,7 @@ public Xid[] recover(int flag) throws XAException {
*
* Postconditions: 1. Transaction is rolled back and disassociated from connection
*/
@Override
public void rollback(Xid xid) throws XAException {
if (LOGGER.isLoggable(Level.FINEST)) {
debug("rolling back xid = " + xid);
Expand All @@ -393,7 +380,7 @@ public void rollback(Xid xid) throws XAException {

try {
if (currentXid != null && xid.equals(currentXid)) {
state = STATE_IDLE;
state = State.IDLE;
currentXid = null;
conn.rollback();
conn.setAutoCommit(localAutoCommitMode);
Expand All @@ -418,6 +405,7 @@ public void rollback(Xid xid) throws XAException {
}
}

@Override
public void commit(Xid xid, boolean onePhase) throws XAException {
if (LOGGER.isLoggable(Level.FINEST)) {
debug("committing xid = " + xid + (onePhase ? " (one phase) " : " (two phase)"));
Expand Down Expand Up @@ -454,12 +442,12 @@ private void commitOnePhase(Xid xid) throws XAException {
"Not implemented: one-phase commit must be issued using the same connection that was used to start it"),
XAException.XAER_RMERR);
}
if (state != STATE_ENDED) {
if (state != State.ENDED) {
throw new PGXAException(GT.tr("commit called before end"), XAException.XAER_PROTO);
}

// Preconditions are met. Commit
state = STATE_IDLE;
state = State.IDLE;
currentXid = null;

conn.commit();
Expand All @@ -481,7 +469,7 @@ private void commitPrepared(Xid xid) throws XAException {
// Check preconditions. The connection mustn't be used for another
// other XA or local transaction, or the COMMIT PREPARED command
// would mess it up.
if (state != STATE_IDLE
if (state != State.IDLE
|| conn.getTransactionState() != TransactionState.IDLE) {
throw new PGXAException(
GT.tr("Not implemented: 2nd phase commit must be issued using an idle connection"),
Expand All @@ -505,6 +493,7 @@ private void commitPrepared(Xid xid) throws XAException {
}
}

@Override
public boolean isSameRM(XAResource xares) throws XAException {
// This trivial implementation makes sure that the
// application server doesn't try to use another connection
Expand All @@ -515,6 +504,7 @@ public boolean isSameRM(XAResource xares) throws XAException {
/**
* Does nothing, since we don't do heuristics,
*/
@Override
public void forget(Xid xid) throws XAException {
throw new PGXAException(GT.tr("Heuristic commit/rollback not supported"),
XAException.XAER_NOTA);
Expand All @@ -523,14 +513,39 @@ public void forget(Xid xid) throws XAException {
/**
* We don't do transaction timeouts. Just returns 0.
*/
@Override
public int getTransactionTimeout() {
return 0;
}

/**
* We don't do transaction timeouts. Returns false.
*/
@Override
public boolean setTransactionTimeout(int seconds) {
return false;
}

private enum State {
/**
* {@code PGXAConnection} not associated with a XA-transaction. You can still call {@link #getConnection()} and
* use the connection outside XA. {@code currentXid} is {@code null}. autoCommit is {@code true} on a connection
* by getConnection, per normal JDBC rules, though the caller can change it to {@code false} and manage
* transactions itself using Connection.commit and rollback.
*/
IDLE,
/**
* {@link #start(Xid, int)} has been called, and we're associated with an XA transaction. {@code currentXid}
* is valid. autoCommit is false on a connection returned by getConnection, and should not be messed with by
* the caller or the XA transaction will be broken.
*/
ACTIVE,
/**
* {@link #end(Xid, int)} has been called, but the transaction has not yet been prepared. {@code currentXid}
* is still valid. You shouldn't use the connection for anything else than issuing a {@link XAResource#commit(Xid, boolean)} or
* rollback.
*/
ENDED
}

}

0 comments on commit 7618822

Please sign in to comment.