blob: de8176bf36b8dee5803602c4f21e83af96074d85 [file] [log] [blame]
/*
* Copyright (c) 2008, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.enterprise.transaction;
import com.sun.enterprise.config.serverbeans.ServerTags;
import com.sun.enterprise.transaction.api.JavaEETransactionManager;
import com.sun.enterprise.transaction.spi.JavaEETransactionManagerDelegate;
import java.beans.PropertyChangeEvent;
import java.util.logging.Level;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.glassfish.api.invocation.InvocationManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.jvnet.hk2.config.UnprocessedChangeEvents;
import jakarta.transaction.InvalidTransactionException;
import jakarta.transaction.NotSupportedException;
import jakarta.transaction.RollbackException;
import jakarta.transaction.Synchronization;
import jakarta.transaction.SystemException;
import jakarta.transaction.Transaction;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.UserTransaction;
import static com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.getStatusAsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Unit test for simple App.
*/
public class JavaEETransactionManagerSimplifiedTest {
private JavaEETransactionManager txManager;
@BeforeEach
public void setUp() {
txManager = new JavaEETransactionManagerSimplified();
JavaEETransactionManagerDelegate delegate = new JavaEETransactionManagerSimplifiedDelegate();
txManager.setDelegate(delegate);
delegate.setTransactionManager(txManager);
}
/**
* Can'txManager test more than null (but no NPE)
*/
@Test
public void testXAResourceWrapper() {
assertNull(txManager.getXAResourceWrapper("xxx"));
assertNull(txManager.getXAResourceWrapper("oracle.jdbc.xa.client.OracleXADataSource"));
}
/**
* Test ConfigListener call
*/
@Test
public void testTransactionServiceConfigListener() {
PropertyChangeEvent e1 = new PropertyChangeEvent("", ServerTags.KEYPOINT_INTERVAL, "1", "10");
PropertyChangeEvent e2 = new PropertyChangeEvent("", ServerTags.RETRY_TIMEOUT_IN_SECONDS, "1", "10");
TransactionServiceConfigListener listener = new TransactionServiceConfigListener();
listener.setTM(txManager);
UnprocessedChangeEvents events = listener.changed(new PropertyChangeEvent[] {e1, e2});
assertNull(events);
}
@Test
public void testWrongResume() throws Exception {
assertThrows(InvalidTransactionException.class, () -> txManager.resume(null),
"WRONG: TM resume null successful");
txManager.begin();
Transaction tx = txManager.getTransaction();
assertThrows(IllegalStateException.class, () -> txManager.resume(null),
"WRONG: TM resume null on active tx successful");
txManager.rollback();
assertThrows(InvalidTransactionException.class, () -> txManager.resume(tx), "WRONG: TM resume successful");
}
@Test
public void testWrongTMOperationsAfterCommit() throws Exception {
txManager.begin();
assertDoesNotThrow(() -> txManager.commit(), "Commit right after begin.");
assertThrows(IllegalStateException.class, () -> txManager.commit(), "Duplicit commit");
assertThrows(IllegalStateException.class, () -> txManager.rollback(), "Rollback after broken commit");
assertThrows(IllegalStateException.class, () -> txManager.setRollbackOnly(),
"WRONG: TM setRollbackOnly successful");
}
@Test
public void testWrongTXOperationsAfterCommit() throws Exception {
txManager.begin();
final Transaction tx = txManager.getTransaction();
txManager.commit();
assertThrows(IllegalStateException.class, () -> tx.commit(), "WRONG: Tx commit successful");
assertThrows(IllegalStateException.class, () -> tx.rollback(), "WRONG: Tx rollback successful");
assertThrows(IllegalStateException.class, () -> tx.setRollbackOnly(), "WRONG: Tx setRollbackOnly successful");
assertThrows(IllegalStateException.class, () -> tx.enlistResource(new TestResource()),
"WRONG: Tx enlistResource successful");
assertThrows(IllegalStateException.class, () -> tx.delistResource(new TestResource(), XAResource.TMSUCCESS),
"WRONG: Tx delistResource successful");
final TestSync s = new TestSync(false);
assertThrows(IllegalStateException.class, () -> tx.registerSynchronization(s),
"WRONG: Tx registerSynchronization successful");
}
@Test
public void testWrongTMCommit() {
assertThrows(IllegalStateException.class, () -> txManager.commit(), "commit without transaction");
}
@Test
public void testWrongTMRollback() {
assertThrows(IllegalStateException.class, () -> txManager.rollback(), "rollback without transaction");
}
@Test
public void testWrongTMTimeout() {
assertThrows(SystemException.class, () -> txManager.setTransactionTimeout(-1),
"WRONG: TM setTransactionTimeout successful with negative value");
}
@Test
public void testWrongUTXTimeout() {
final UserTransaction utx = createUtx();
assertThrows(SystemException.class, () -> utx.setTransactionTimeout(-1),
"WRONG: UTX setTransactionTimeout successful with negative value");
}
@Test
public void testWrongUTXCommit() {
final UserTransaction utx = createUtx();
assertThrows(IllegalStateException.class, () -> utx.commit(), "WRONG: UTX commit successful");
}
@Test
public void testWrongUTXBegin() throws Exception {
final UserTransaction utx = createUtx();
utx.begin();
assertThrows(NotSupportedException.class, () -> utx.begin(), "WRONG: TWICE UTX begin successful");
}
@Test
public void testWrongUTXOperationsAfterCommit() throws Exception {
txManager.begin();
UserTransaction utx = createUtx();
txManager.commit();
assertThrows(IllegalStateException.class, () -> utx.commit(), "WRONG: UTx commit successful");
assertThrows(IllegalStateException.class, () -> utx.rollback(), "WRONG: UTx rollback successful");
assertThrows(IllegalStateException.class, () -> utx.setRollbackOnly(), "WRONG: UTx setRollbackOnly successful");
}
@Test
public void testBegin() throws Exception {
assertEquals("NoTransaction", getStatusAsString(txManager.getStatus()));
txManager.begin();
assertEquals("Active", getStatusAsString(txManager.getStatus()));
}
@Test
public void testCommit() throws Exception {
txManager.begin();
assertEquals("Active", getStatusAsString(txManager.getStatus()));
txManager.commit();
assertEquals("NoTransaction", getStatusAsString(txManager.getStatus()));
}
@Test
public void testRollback() throws Exception {
txManager.begin();
assertEquals("Active", getStatusAsString(txManager.getStatus()));
txManager.rollback();
assertEquals("NoTransaction", getStatusAsString(txManager.getStatus()));
}
@Test
public void testTxCommit() throws Exception {
txManager.begin();
Transaction tx = txManager.getTransaction();
TestSync sync = new TestSync(false);
tx.registerSynchronization(sync);
assertEquals("Active", getStatusAsString(tx.getStatus()));
tx.commit();
assertAll(
() -> assertEquals("Committed", getStatusAsString(tx.getStatus())),
() -> assertEquals("NoTransaction", getStatusAsString(txManager.getStatus())),
() -> assertTrue(sync.called_beforeCompletion, "beforeCompletion was not called"),
() -> assertTrue(sync.called_afterCompletion, "afterCompletion was not called")
);
}
@Test
public void testTxSuspendResume() throws Exception {
assertNull(txManager.suspend());
txManager.begin();
Transaction tx = txManager.suspend();
assertNotNull(tx);
assertNull(txManager.suspend());
txManager.resume(tx);
assertEquals("Active", getStatusAsString(tx.getStatus()));
tx.commit();
assertEquals("Committed", getStatusAsString(tx.getStatus()));
assertEquals("NoTransaction", getStatusAsString(txManager.getStatus()));
}
@Test
public void testTxRollback() throws Exception {
txManager.begin();
Transaction tx = txManager.getTransaction();
TestSync sync = new TestSync(false);
tx.registerSynchronization(sync);
assertEquals("Active", getStatusAsString(tx.getStatus()));
tx.rollback();
assertAll(
() -> assertEquals("RolledBack", getStatusAsString(tx.getStatus())),
() -> assertEquals("NoTransaction", getStatusAsString(txManager.getStatus())),
() -> assertFalse(sync.called_beforeCompletion, "beforeCompletion was called"),
() -> assertTrue(sync.called_afterCompletion, "afterCompletion was not called")
);
}
@Test
public void testUTxCommit() throws Exception {
UserTransaction utx = createUtx();
utx.begin();
assertEquals("Active", getStatusAsString(txManager.getStatus()));
utx.commit();
assertEquals("NoTransaction", getStatusAsString(txManager.getStatus()));
}
@Test
public void testUTxRollback() throws Exception {
UserTransaction utx = createUtx();
utx.begin();
assertEquals("Active", getStatusAsString(utx.getStatus()));
utx.rollback();
assertEquals("NoTransaction", getStatusAsString(utx.getStatus()));
}
@Test
public void testTxCommitFailBeforeCompletion() throws Exception {
// Suppress warnings from beforeCompletion() logging
((JavaEETransactionManagerSimplified) txManager).getLogger().setLevel(Level.SEVERE);
txManager.begin();
final Transaction tx = txManager.getTransaction();
final TestSync s = new TestSync(true);
tx.registerSynchronization(s);
assertEquals("Active", getStatusAsString(txManager.getStatus()));
final RollbackException rollbackException = assertThrows(RollbackException.class, () -> tx.commit());
assertAll(
() -> assertThat("rollbackException cause", rollbackException.getCause(), instanceOf(MyRuntimeException.class)),
() -> assertEquals("RolledBack", getStatusAsString(tx.getStatus())),
() -> assertTrue(s.called_beforeCompletion, "beforeCompletion was not called"),
() -> assertTrue(s.called_afterCompletion, "afterCompletion was not called")
);
}
@Test
public void testTMCommitFailBeforeCompletion() throws Exception {
// Suppress warnings from beforeCompletion() logging
((JavaEETransactionManagerSimplified) txManager).getLogger().setLevel(Level.SEVERE);
txManager.begin();
final Transaction tx = txManager.getTransaction();
final TestSync s = new TestSync(true);
tx.registerSynchronization(s);
assertEquals("Active", getStatusAsString(txManager.getStatus()));
final RollbackException rollbackException = assertThrows(RollbackException.class, () -> txManager.commit());
assertAll(
() -> assertThat("rollbackException cause", rollbackException.getCause(), instanceOf(MyRuntimeException.class)),
() -> assertEquals("RolledBack", getStatusAsString(tx.getStatus())),
() -> assertTrue(s.called_beforeCompletion, "beforeCompletion was not called"),
() -> assertTrue(s.called_afterCompletion, "afterCompletion was not called")
);
}
@Test
public void testTxCommitFailInterposedSyncBeforeCompletion() throws Exception {
// Suppress warnings from beforeCompletion() logging
((JavaEETransactionManagerSimplified) txManager).getLogger().setLevel(Level.SEVERE);
txManager.begin();
Transaction tx = txManager.getTransaction();
TestSync s = new TestSync(true);
((JavaEETransactionImpl) tx).registerInterposedSynchronization(s);
assertEquals("Active", getStatusAsString(txManager.getStatus()));
final RollbackException rollbackException = assertThrows(RollbackException.class, () -> tx.commit());
assertAll(
() -> assertThat("rollbackException cause", rollbackException.getCause(), instanceOf(MyRuntimeException.class)),
() -> assertEquals("RolledBack", getStatusAsString(tx.getStatus())),
() -> assertTrue(s.called_beforeCompletion, "beforeCompletion was not called"),
() -> assertTrue(s.called_afterCompletion, "afterCompletion was not called")
);
}
@Test
public void testTxCommitRollbackBeforeCompletion() throws Exception {
// Suppress warnings from beforeCompletion() logging
((JavaEETransactionManagerSimplified) txManager).getLogger().setLevel(Level.SEVERE);
txManager.begin();
Transaction tx = txManager.getTransaction();
TestSync s = new TestSync(txManager);
tx.registerSynchronization(s);
assertEquals("Active", getStatusAsString(txManager.getStatus()));
final RollbackException rollbackException = assertThrows(RollbackException.class, () -> tx.commit());
assertAll(
() -> assertNull(rollbackException.getCause(), "rollbackException cause"),
() -> assertEquals("RolledBack", getStatusAsString(tx.getStatus())),
() -> assertTrue(s.called_beforeCompletion, "beforeCompletion was not called"),
() -> assertTrue(s.called_afterCompletion, "afterCompletion was not called")
);
}
private UserTransaction createUtx() {
UserTransaction utx = new UserTransactionImpl();
InvocationManager im = new org.glassfish.api.invocation.InvocationManagerImpl();
((UserTransactionImpl) utx).setForTesting(txManager, im);
return utx;
}
static class TestSync implements Synchronization {
// Used to validate the calls
private final boolean fail;
private TransactionManager t;
protected boolean called_beforeCompletion;
protected boolean called_afterCompletion;
public TestSync(boolean fail) {
this.fail = fail;
}
public TestSync(TransactionManager t) {
fail = true;
this.t = t;
}
@Override
public void beforeCompletion() {
System.out.println("**Called beforeCompletion **");
called_beforeCompletion = true;
if (fail) {
System.out.println("**Failing in beforeCompletion **");
if (t != null) {
try {
System.out.println("**Calling setRollbackOnly **");
t.setRollbackOnly();
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("**Throwing MyRuntimeException... **");
throw new MyRuntimeException("test");
}
}
}
@Override
public void afterCompletion(int status) {
System.out.println("**Called afterCompletion with status: " + getStatusAsString(status));
called_afterCompletion = true;
}
}
static class MyRuntimeException extends RuntimeException {
public MyRuntimeException(String msg) {
super(msg);
}
}
static class TestResource implements XAResource {
@Override
public void commit(Xid xid, boolean onePhase) throws XAException {
}
@Override
public boolean isSameRM(XAResource xaresource) throws XAException {
return false;
}
@Override
public void rollback(Xid xid) throws XAException {
}
@Override
public int prepare(Xid xid) throws XAException {
return XAResource.XA_OK;
}
@Override
public boolean setTransactionTimeout(int i) throws XAException {
return true;
}
@Override
public int getTransactionTimeout() throws XAException {
return 0;
}
@Override
public void forget(Xid xid) throws XAException {
}
@Override
public void start(Xid xid, int flags) throws XAException {
}
@Override
public void end(Xid xid, int flags) throws XAException {
}
@Override
public Xid[] recover(int flags) throws XAException {
return null;
}
}
}