Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firing CDI events on BeforeCommit, AfterCommit and AfterRollback. #30

Merged
merged 5 commits into from
Feb 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
.classpath
.settings
target

.DS_Store

bin/
31 changes: 31 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,37 @@ To do that you just need to add the follow content into your project's `beans.xm
</decorators>
```

# CDI Events

While the JPATransactionInterceptor worries about handling the transaction, you can observe CDI events to include some logic of yours.

**Please, note that if you specializes or override the JPATransactionInterceptor, those events won't be fired.**

*Remember that CDI Events doesn't have an order when executing observers, so, when observing the same event in more than one method, they should be independents.*

The events are:
* Before trying to commit the transaction: `BeforeCommit`;
* After successfully committing the transaction: `AfterCommit`;
* After successfully rolling back (rollback) the transaction: `AfterRollback`;

```Java
import javax.enterprise.event.Observes;

import br.com.caelum.vraptor.jpa.event.BeforeCommit;
import br.com.caelum.vraptor.jpa.event.AfterCommit;
import br.com.caelum.vraptor.jpa.event.AfterRollback;

public class JPATransactionEventsObserver {
/* You can @Inject any dependencies here. */

public void beforeCommit(@Observes BeforeCommit before) {/* ... */}

public void afterCommit(@Observes AfterCommit after) {/* ... */}

public void afterRollback(@Observes AfterRollback after) {/* ... */}
}
```

# Help

Get help from vraptor developers and the community at VRaptor's mailing list.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package br.com.caelum.vraptor.jpa;

import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
Expand All @@ -24,6 +25,9 @@
import br.com.caelum.vraptor.Intercepts;
import br.com.caelum.vraptor.http.MutableResponse;
import br.com.caelum.vraptor.interceptor.SimpleInterceptorStack;
import br.com.caelum.vraptor.jpa.event.AfterCommit;
import br.com.caelum.vraptor.jpa.event.AfterRollback;
import br.com.caelum.vraptor.jpa.event.BeforeCommit;
import br.com.caelum.vraptor.validator.Validator;

/**
Expand All @@ -36,6 +40,7 @@
@Intercepts
public class JPATransactionInterceptor implements JPAInterceptor {

private final BeanManager beanManager;
private final EntityManager manager;
private final Validator validator;
private final MutableResponse response;
Expand All @@ -44,11 +49,12 @@ public class JPATransactionInterceptor implements JPAInterceptor {
* @deprecated CDI eyes only.
*/
protected JPATransactionInterceptor() {
this(null, null, null);
this(null, null, null, null);
}

@Inject
public JPATransactionInterceptor(EntityManager manager, Validator validator, MutableResponse response) {
public JPATransactionInterceptor(BeanManager beanManager, EntityManager manager, Validator validator, MutableResponse response) {
this.beanManager = beanManager;
this.manager = manager;
this.validator = validator;
this.response = response;
Expand All @@ -71,13 +77,18 @@ public void intercept(SimpleInterceptorStack stack) {
} finally {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
beanManager.fireEvent(new AfterRollback());
}
}
}

private void commit(EntityTransaction transaction) {
if (transaction.isActive())
beanManager.fireEvent(new BeforeCommit());

if (!validator.hasErrors() && transaction.isActive()) {
transaction.commit();
beanManager.fireEvent(new AfterCommit());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package br.com.caelum.vraptor.jpa.event;

public class AfterCommit {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package br.com.caelum.vraptor.jpa.event;

public class AfterRollback {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package br.com.caelum.vraptor.jpa.event;

public class BeforeCommit {}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package br.com.caelum.vraptor.jpa;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import javax.enterprise.inject.spi.BeanManager;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

Expand All @@ -14,14 +17,20 @@
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import br.com.caelum.vraptor.controller.ControllerMethod;
import br.com.caelum.vraptor.http.MutableResponse;
import br.com.caelum.vraptor.interceptor.SimpleInterceptorStack;
import br.com.caelum.vraptor.jpa.event.AfterCommit;
import br.com.caelum.vraptor.jpa.event.AfterRollback;
import br.com.caelum.vraptor.jpa.event.BeforeCommit;
import br.com.caelum.vraptor.validator.Validator;

public class JPATransactionInterceptorTest {

@Mock private BeanManager beanManager;
@Mock private EntityManager entityManager;
@Mock private SimpleInterceptorStack stack;
@Mock private ControllerMethod method;
Expand All @@ -34,33 +43,52 @@ public class JPATransactionInterceptorTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
interceptor = new JPATransactionInterceptor(entityManager, validator, response);
interceptor = new JPATransactionInterceptor(beanManager, entityManager, validator, response);

// Returns false when transaction.isActive() is called after committing the transaction.
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
when(transaction.isActive()).thenReturn(false);
return null;
}
}).when(transaction).commit();
}

@Test
public void shouldStartAndCommitTransaction() throws Exception {

when(entityManager.getTransaction()).thenReturn(transaction);
when(transaction.isActive()).thenReturn(true);
when(validator.hasErrors()).thenReturn(false);

interceptor.intercept(stack);

InOrder callOrder = inOrder(entityManager, transaction, stack);
InOrder callOrder = inOrder(beanManager, entityManager, transaction, stack);
callOrder.verify(entityManager).getTransaction();
callOrder.verify(transaction).begin();
callOrder.verify(stack).next();
callOrder.verify(beanManager).fireEvent(isA(BeforeCommit.class));
callOrder.verify(transaction).commit();
callOrder.verify(beanManager).fireEvent(isA(AfterCommit.class));

verify(beanManager, never()).fireEvent(isA(AfterRollback.class));
}

@Test
public void shouldRollbackTransactionIfStillActiveWhenExecutionFinishes() throws Exception {

when(entityManager.getTransaction()).thenReturn(transaction);
when(transaction.isActive()).thenReturn(true);
when(validator.hasErrors()).thenReturn(true);

interceptor.intercept(stack);

verify(transaction).rollback();

verify(beanManager).fireEvent(isA(BeforeCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterCommit.class));
verify(beanManager).fireEvent(isA(AfterRollback.class));
}

@Test
Expand All @@ -73,6 +101,10 @@ public void shouldRollbackIfValidatorHasErrors() {
interceptor.intercept(stack);

verify(transaction).rollback();

verify(beanManager).fireEvent(isA(BeforeCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterCommit.class));
verify(beanManager).fireEvent(isA(AfterRollback.class));
}

@Test
Expand All @@ -85,6 +117,10 @@ public void shouldCommitIfValidatorHasNoErrors() {
interceptor.intercept(stack);

verify(transaction).commit();

verify(beanManager).fireEvent(isA(BeforeCommit.class));
verify(beanManager).fireEvent(isA(AfterCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterRollback.class));
}

@Test
Expand All @@ -96,15 +132,42 @@ public void doNothingIfHasNoActiveTransation() {
interceptor.intercept(stack);

verify(transaction, never()).rollback();
verify(transaction, never()).commit();

verify(beanManager, never()).fireEvent(isA(BeforeCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterRollback.class));
}

@Test
public void shouldConfigureARedirectListener() {
public void shouldConfigureARedirectListenerWhenTransactionIsCommited() {

when(entityManager.getTransaction()).thenReturn(transaction);
when(transaction.isActive()).thenReturn(true);
when(validator.hasErrors()).thenReturn(false);

interceptor.intercept(stack);

verify(response).addRedirectListener(any(MutableResponse.RedirectListener.class));

verify(beanManager).fireEvent(isA(BeforeCommit.class));
verify(beanManager).fireEvent(isA(AfterCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterRollback.class));
}

@Test
public void shouldConfigureARedirectListenerWhenTransactionIsRolledback() {

when(entityManager.getTransaction()).thenReturn(transaction);
when(transaction.isActive()).thenReturn(true);
when(validator.hasErrors()).thenReturn(true);

interceptor.intercept(stack);

verify(response).addRedirectListener(any(MutableResponse.RedirectListener.class));

verify(beanManager).fireEvent(isA(BeforeCommit.class));
verify(beanManager, never()).fireEvent(isA(AfterCommit.class));
verify(beanManager).fireEvent(isA(AfterRollback.class));
}
}