/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed 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.compass.core.transaction;
import javax.transaction.Transaction;
import javax.transaction.UserTransaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.core.engine.SearchEngine;
import org.compass.core.spi.InternalCompassSession;
/**
* <p>Allows for Compass to particiapte in a two phase commit transaction using
* JTA.
*
* <p>Enlists an {@link javax.transaction.xa.XAResource} Compass implementation
* with a current JTA {@link javax.transaction.Transaction}.
*
* <p>Transaction management is done by {@link org.compass.core.transaction.XATransactionFactory}
* so there is no need to implement suspend and resume (works the same way
* {@link org.compass.core.transaction.JTASyncTransaction} does).
*
* @author kimchy
*/
public class XATransaction extends AbstractJTATransaction {
public XATransaction(UserTransaction ut, TransactionFactory transactionFactory) {
super(ut, transactionFactory);
}
protected void doBindToTransaction(Transaction tx, InternalCompassSession session, boolean newTransaction) throws Exception {
tx.enlistResource(new CompassXAResource(session));
}
private static class CompassXAResource implements XAResource {
private static final Log log = LogFactory.getLog(CompassXAResource.class);
private InternalCompassSession session;
private SearchEngine searchEngine;
public CompassXAResource(InternalCompassSession session) {
this.session = session;
this.searchEngine = session.getSearchEngine();
}
public int getTransactionTimeout() throws XAException {
return 0;
}
public boolean setTransactionTimeout(int i) throws XAException {
// TODO support transaction timeout
return false;
}
public boolean isSameRM(XAResource xares) throws XAException {
return (xares != null && xares instanceof CompassXAResource && session == ((CompassXAResource) xares).session);
}
public Xid[] recover(int i) throws XAException {
return null;
}
public void forget(Xid xid) throws XAException {
session.close();
}
public void start(Xid xid, int flags) throws XAException {
switch (flags) {
case TMJOIN:
case TMRESUME:
// no need to resume anything, the outer
// transaction factory manages it
case TMNOFLAGS:
// no need to start the transaction here
// since we already started it in the base class
default:
break;
}
}
public void end(Xid xid, int flags) throws XAException {
// nothing here to do
switch (flags) {
case TMSUSPEND:
break;
case TMFAIL:
session.unbindTransaction();
break;
case TMSUCCESS:
session.unbindTransaction();
break;
}
}
public int prepare(Xid xid) throws XAException {
try {
searchEngine.prepare();
} catch (Exception e) {
log.error("Failed to prepare transaction [" + xid + "]", e);
throw new XAException(e.getMessage());
}
if (searchEngine.onlyReadOperations()) {
commit(xid, false);
return XA_RDONLY;
}
return XA_OK;
}
public void commit(Xid xid, boolean onePhase) throws XAException {
if (searchEngine.wasRolledBack()) {
throw new XAException(XAException.XA_RBROLLBACK);
}
try {
searchEngine.commit(onePhase);
} catch (Exception e) {
log.error("Failed to commit transaction [" + xid + "]", e);
throw new XAException(e.getMessage());
} finally {
session.unbindTransaction();
}
}
public void rollback(Xid xid) throws XAException {
try {
searchEngine.rollback();
} catch (Exception e) {
log.error("Failed to rollback transaction [" + xid + "]", e);
throw new XAException(e.getMessage());
} finally {
session.unbindTransaction();
}
}
}
}