479256: Transactional Resources Leak in ServerJob 

Change-Id: If489065b0439a910cc2efd400900e6d7f20d4575
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=479256
Reviewed-on: https://git.eclipse.org/r/57652
Reviewed-by: Andi Bur <andi.bur@gmail.com>
Tested-by: Andi Bur <andi.bur@gmail.com>
diff --git a/org.eclipse.scout.rt.server.test/src/org/eclipse/scout/rt/server/ServerJobTest.java b/org.eclipse.scout.rt.server.test/src/org/eclipse/scout/rt/server/ServerJobTest.java
index e56b7ab..8d10503 100644
--- a/org.eclipse.scout.rt.server.test/src/org/eclipse/scout/rt/server/ServerJobTest.java
+++ b/org.eclipse.scout.rt.server.test/src/org/eclipse/scout/rt/server/ServerJobTest.java
@@ -11,7 +11,10 @@
 package org.eclipse.scout.rt.server;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -25,9 +28,17 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.scout.commons.exception.ProcessingException;
+import org.eclipse.scout.rt.server.internal.Activator;
 import org.eclipse.scout.rt.server.transaction.ITransaction;
+import org.eclipse.scout.rt.server.transaction.ITransactionMember;
+import org.eclipse.scout.rt.shared.services.common.exceptionhandler.IExceptionHandlerService;
 import org.eclipse.scout.rt.testing.shared.HandlerAdapter;
+import org.eclipse.scout.rt.testing.shared.TestingUtility;
 import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.osgi.framework.ServiceRegistration;
 
 /**
  * Tests for {@link ServerJob}
@@ -36,6 +47,7 @@
  */
 public class ServerJobTest {
 
+  private static final ProcessingException EXPECTED_PROCESSING_EXCEPTION = new ProcessingException("Expected exception");
   private static final RuntimeException m_rollBackException = new RuntimeException("Exception in transaction.rollBack()!");
   private static final Throwable[] m_transactionFailures = new Throwable[2];
   static {
@@ -54,6 +66,51 @@
     assertEquals("Could not find transactionFailure1 in the Logs!", true, interceptor.hasExceptionLogged(m_transactionFailures[1]));
   }
 
+  @Test
+  public void testRollbackAndRelease() throws Exception {
+    doTestRollbackAndRelease();
+  }
+
+  @Test
+  public void testRollbackAndReleaseUsingCustomExceptionHandlerService() throws Exception {
+    final IExceptionHandlerService exceptionHandlerService = mock(IExceptionHandlerService.class);
+    Mockito.doThrow(new RuntimeException("translated exception")).when(exceptionHandlerService).handleException(any(ProcessingException.class));
+    List<ServiceRegistration> testServices = TestingUtility.registerServices(Activator.getDefault().getBundle(), 1000, exceptionHandlerService);
+
+    try {
+      doTestRollbackAndRelease();
+    }
+    finally {
+      TestingUtility.unregisterServices(testServices);
+    }
+  }
+
+  protected void doTestRollbackAndRelease() {
+    final ITransactionMember txnMember = mock(ITransactionMember.class);
+    when(txnMember.needsCommit()).thenReturn(true);
+
+    final ServerJob job = new ServerJob("test rollback and release job", mock(IServerSession.class)) {
+      @Override
+      protected IStatus runTransaction(IProgressMonitor monitor) throws Exception {
+        ITransaction txn = ThreadContext.getTransaction();
+        txn.registerMember(txnMember);
+        throw EXPECTED_PROCESSING_EXCEPTION;
+      }
+    };
+
+    job.runNow(new NullProgressMonitor());
+    try {
+      job.throwOnError();
+    }
+    catch (ProcessingException e) {
+      assertSame(EXPECTED_PROCESSING_EXCEPTION, e);
+    }
+
+    InOrder order = inOrder(txnMember);
+    order.verify(txnMember).rollback();
+    order.verify(txnMember).release();
+  }
+
   private P_LogHandler installLogInterceptor() {
     Logger logger = LogManager.getLogManager().getLogger(ServerJob.class.getName());
     P_LogHandler handler = new P_LogHandler();
@@ -80,7 +137,6 @@
       doThrow(m_rollBackException).when(transaction).rollback(); //Throw unchecked exception here!
       return transaction;
     }
-
   }
 
   private class P_LogHandler extends HandlerAdapter {
@@ -101,5 +157,4 @@
       return false;
     }
   }
-
 }
diff --git a/org.eclipse.scout.rt.server/src/org/eclipse/scout/rt/server/ServerJob.java b/org.eclipse.scout.rt.server/src/org/eclipse/scout/rt/server/ServerJob.java
index 2861d1e..2bf1765 100644
--- a/org.eclipse.scout.rt.server/src/org/eclipse/scout/rt/server/ServerJob.java
+++ b/org.eclipse.scout.rt.server/src/org/eclipse/scout/rt/server/ServerJob.java
@@ -251,25 +251,31 @@
     finally {
       ActiveTransactionRegistry.unregister(transaction);
       if (transaction.hasFailures()) {
-        try {
-          IExceptionHandlerService exceptionHandlerService = SERVICES.getService(IExceptionHandlerService.class);
-          for (Throwable transactionFailure : transaction.getFailures()) {
-            if (transactionFailure instanceof ProcessingException) {
+        IExceptionHandlerService exceptionHandlerService = SERVICES.getService(IExceptionHandlerService.class);
+        for (Throwable transactionFailure : transaction.getFailures()) {
+          if (transactionFailure instanceof ProcessingException) {
+            try {
               exceptionHandlerService.handleException((ProcessingException) transactionFailure);
             }
-            else {
-              LOG.error("Transaction had failure.", transactionFailure);
+            catch (Throwable t) {
+              if (LOG.isDebugEnabled()) {
+                LOG.warn("Handling transaction failure throwed another exception", t);
+              }
+              else {
+                LOG.warn("Handling transaction failure throwed another exception of type '{}'", t.getClass().getName());
+              }
             }
           }
+          else {
+            LOG.error("Transaction had failure.", transactionFailure);
+          }
         }
-        finally {
-          // xa rollback
-          try {
-            transaction.rollback();
-          }
-          catch (Throwable t) {
-            LOG.error("Transaction rollback failed with exception.", t);
-          }
+        // xa rollback
+        try {
+          transaction.rollback();
+        }
+        catch (Throwable t) {
+          LOG.error("Transaction rollback failed with exception.", t);
         }
       }
       else {