/**
* Copyright 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.springframework.extensions.jcr.jackrabbit;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import junit.framework.TestCase;
import org.apache.jackrabbit.api.XASession;
import org.junit.Test;
import org.springframework.extensions.jcr.JcrCallback;
import org.springframework.extensions.jcr.JcrTemplate;
import org.springframework.extensions.jcr.SessionFactory;
import org.springframework.extensions.jcr.jackrabbit.support.UserTxSessionHolder;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
/**
* @author Costin Leau
* @author Sergio Bossa
* @author Salvatore Incandela
*/
public class LocalTransactionManagerTest {
@Test
public void testTransactionCommit() throws Exception {
final SessionFactory sessionFactory = createMock(SessionFactory.class);
final XASession xaSession = createMock(XASession.class);
// create nice mock
XAResource xaResource = createMock(XAResource.class);
expect(sessionFactory.getSession()).andReturn(xaSession);
expect(xaSession.getXAResource()).andReturn(xaResource);
xaSession.save();
xaSession.logout();
Xid xidMock = new XidMock();
xaResource.start(xidMock, XAResource.TMNOFLAGS);
expect(xaResource.prepare(xidMock)).andReturn(0);
xaResource.commit(xidMock, false);
xaResource.end(xidMock, XAResource.TMSUCCESS);
replay(sessionFactory,xaSession,xaResource);
PlatformTransactionManager tm = new LocalTransactionManager(sessionFactory);
TransactionTemplate tt = new TransactionTemplate(tm);
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
tt.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
JcrTemplate template = new JcrTemplate(sessionFactory);
template.save();
}
});
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
verify(sessionFactory, xaResource, xaSession);
}
@Test
public void testTransactionRollback() throws Exception {
final SessionFactory sessionFactory = createMock(SessionFactory.class);
final XASession xaSession = createMock(XASession.class);
XAResource xaResource = createMock(XAResource.class);
expect(sessionFactory.getSession()).andReturn(xaSession);
expect(xaSession.getXAResource()).andReturn(xaResource);
xaSession.save();
xaSession.logout();
Xid xidMock = new XidMock();
xaResource.start(xidMock, XAResource.TMNOFLAGS);
xaResource.end(xidMock, XAResource.TMFAIL);
xaResource.rollback(xidMock);
replay(sessionFactory,xaSession,xaResource);
PlatformTransactionManager tm = new LocalTransactionManager(sessionFactory);
TransactionTemplate tt = new TransactionTemplate(tm);
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
try {
tt.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
JcrTemplate template = new JcrTemplate(sessionFactory);
template.execute(new JcrCallback<Object>() {
public Object doInJcr(Session se) throws RepositoryException {
se.save();
throw new RuntimeException();
}
});
}
});
} catch (RuntimeException e) {
// it's okay
}
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
verify(sessionFactory, xaSession, xaResource);
}
@Test
public void testTransactionRollbackOnly() throws Exception {
final SessionFactory sessionFactory = createMock(SessionFactory.class);
final XASession xaSession = createMock(XASession.class);
XAResource xaResource = createMock(XAResource.class);
expect(sessionFactory.getSession()).andReturn(xaSession);
expect(xaSession.getXAResource()).andReturn(xaResource);
xaSession.save();
xaSession.logout();
Xid xidMock = new XidMock();
xaResource.start(xidMock, XAResource.TMNOFLAGS);
xaResource.end(xidMock, XAResource.TMFAIL);
xaResource.rollback(xidMock);
replay(sessionFactory, xaSession, xaResource);
PlatformTransactionManager transactionManager = new LocalTransactionManager(sessionFactory);
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
JcrTemplate template = new JcrTemplate(sessionFactory);
template.execute(new JcrCallback<Object>() {
public Object doInJcr(Session se) throws RepositoryException {
se.save();
return null;
}
});
status.setRollbackOnly();
}
});
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
verify(sessionFactory, xaSession, xaResource);
}
@Test
public void testInvalidIsolation() throws Exception {
final SessionFactory sessionFactory = createMock(SessionFactory.class);
replay(sessionFactory);
PlatformTransactionManager transactionManager = new LocalTransactionManager(sessionFactory);
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
try {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
JcrTemplate template = new JcrTemplate(sessionFactory);
template.execute(new JcrCallback<Object>() {
public Object doInJcr(Session session) throws RepositoryException {
return null;
}
});
}
});
fail("Should have thrown InvalidIsolationLevelException");
} catch (InvalidIsolationLevelException e) {
// it's okay
}
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sessionFactory));
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
verify(sessionFactory);
}
@Test
public void testTransactionCommitWithPrebound() throws Exception {
final SessionFactory sessionFactory = createMock(SessionFactory.class);
final XASession xaSession = createMock(XASession.class);
XAResource xaResource = createMock(XAResource.class);
expect(xaSession.getXAResource()).andReturn(xaResource);
xaSession.save();
replay(sessionFactory, xaSession, xaResource);
PlatformTransactionManager transactionManager = new LocalTransactionManager(sessionFactory);
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
UserTxSessionHolder userTransaction = new UserTxSessionHolder(xaSession);
TransactionSynchronizationManager.bindResource(sessionFactory, userTransaction);
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
JcrTemplate template = new JcrTemplate(sessionFactory);
template.save();
}
});
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
TransactionSynchronizationManager.unbindResource(sessionFactory);
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
verify(sessionFactory, xaSession, xaResource);
}
@Test
public void testTransactionRollbackOnlyWithPrebound() throws Exception {
final SessionFactory sessionFactory = createMock(SessionFactory.class);
final XASession xaSession = createMock(XASession.class);
XAResource xaResource = createMock(XAResource.class);
expect(xaSession.getXAResource()).andReturn(xaResource);
xaSession.save();
replay(sessionFactory, xaSession, xaResource);
PlatformTransactionManager transactionManager = new LocalTransactionManager(sessionFactory);
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
UserTxSessionHolder userTransaction = new UserTxSessionHolder(xaSession);
TransactionSynchronizationManager.bindResource(sessionFactory, userTransaction);
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
userTransaction.setRollbackOnly();
try {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
JcrTemplate template = new JcrTemplate(sessionFactory);
template.save();
}
});
} catch (UnexpectedRollbackException e) {
System.out.println(e);
}
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sessionFactory));
TransactionSynchronizationManager.unbindResource(sessionFactory);
assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
verify(sessionFactory, xaSession, xaResource);
}
/**
* Simple mock which overrides equals.
* @author Costin Leau
* @author Sergio Bossa
* @author Salvatore Incandela
*/
protected class XidMock implements Xid {
/**
* @see javax.transaction.xa.Xid#getBranchQualifier()
*/
public byte[] getBranchQualifier() {
return null;
}
/**
* @see javax.transaction.xa.Xid#getFormatId()
*/
public int getFormatId() {
return 0;
}
/**
* @see javax.transaction.xa.Xid#getGlobalTransactionId()
*/
public byte[] getGlobalTransactionId() {
return null;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
return true;
}
}
}