forked from google/guice
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
google#731 Add @RequiresUnitOfWork annotation
- Loading branch information
1 parent
abc78c3
commit 6194e67
Showing
7 changed files
with
221 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
extensions/persist/src/com/google/inject/persist/RequiresUnitOfWork.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.google.inject.persist; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* <p> Any method or class marked with this annotation will require the existence | ||
* of a unit of work. | ||
* <p>Marking a method {@code @RequiresUnitOfWork} will start a unit of work if none | ||
* exists before the method executes and end it if it was started after the method returns. | ||
*/ | ||
@Target({ ElementType.METHOD, ElementType.TYPE }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Inherited | ||
public @interface RequiresUnitOfWork { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
extensions/persist/src/com/google/inject/persist/jpa/RequiresUnitOfWorkInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.google.inject.persist.jpa; | ||
|
||
import java.lang.reflect.Method; | ||
|
||
import org.aopalliance.intercept.MethodInterceptor; | ||
import org.aopalliance.intercept.MethodInvocation; | ||
|
||
import com.google.inject.Inject; | ||
import com.google.inject.persist.RequiresUnitOfWork; | ||
|
||
public class RequiresUnitOfWorkInterceptor implements MethodInterceptor { | ||
@Inject | ||
private UnitOfWorkHandler unitOfWorkHandler; | ||
|
||
public Object invoke(MethodInvocation methodInvocation) throws Throwable { | ||
RequiresUnitOfWork annotation = readAnnotationMetadata(methodInvocation); | ||
if (annotation == null) { | ||
// Avoid creating the unit of work in Object class methods | ||
return methodInvocation.proceed(); | ||
|
||
} else { | ||
unitOfWorkHandler.requireUnitOfWork(); | ||
try { | ||
return methodInvocation.proceed(); | ||
} finally { | ||
unitOfWorkHandler.endRequireUnitOfWork(); | ||
} | ||
} | ||
} | ||
|
||
private RequiresUnitOfWork readAnnotationMetadata(MethodInvocation methodInvocation) { | ||
RequiresUnitOfWork annotation; | ||
Method method = methodInvocation.getMethod(); | ||
|
||
// Annotation in method or class | ||
annotation = method.getAnnotation(RequiresUnitOfWork.class); | ||
if (annotation == null) { | ||
annotation = method.getClass().getAnnotation(RequiresUnitOfWork.class); | ||
} | ||
|
||
return annotation; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
extensions/persist/src/com/google/inject/persist/jpa/UnitOfWorkHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.google.inject.persist.jpa; | ||
|
||
import javax.persistence.EntityManager; | ||
|
||
import com.google.inject.Inject; | ||
import com.google.inject.persist.UnitOfWork; | ||
|
||
/** | ||
* Utility class able to handle easily the start and end of units of work | ||
* in a nested context. It starts the unit of work if there isn't any in | ||
* the current thread, and ends it if the unit of work was started by this | ||
* instance. | ||
*/ | ||
public class UnitOfWorkHandler { | ||
private JpaPersistService emProvider = null; | ||
private UnitOfWork unitOfWork = null; | ||
|
||
/** Tracks if the unit of work was begun implicitly by this handler. */ | ||
private final ThreadLocal<Boolean> didWeStartWork = new ThreadLocal<Boolean>(); | ||
|
||
@Inject | ||
public UnitOfWorkHandler(JpaPersistService emProvider, UnitOfWork unitOfWork) { | ||
this.emProvider = emProvider; | ||
this.unitOfWork = unitOfWork; | ||
} | ||
|
||
public void requireUnitOfWork() { | ||
if (!emProvider.isWorking()) { | ||
unitOfWork.begin(); | ||
didWeStartWork.set(true); | ||
} | ||
} | ||
|
||
public void endRequireUnitOfWork() { | ||
if (didWeStartWork.get() != null) { | ||
didWeStartWork.remove(); | ||
unitOfWork.end(); | ||
} | ||
} | ||
|
||
public EntityManager getEntityManager() { | ||
return emProvider.get(); | ||
} | ||
} |
88 changes: 88 additions & 0 deletions
88
extensions/persist/test/com/google/inject/persist/jpa/RequiresUnitOfWorkTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package com.google.inject.persist.jpa; | ||
|
||
import javax.persistence.EntityManager; | ||
import javax.persistence.EntityManagerFactory; | ||
|
||
import junit.framework.TestCase; | ||
|
||
import com.google.inject.Guice; | ||
import com.google.inject.Inject; | ||
import com.google.inject.Injector; | ||
import com.google.inject.Provider; | ||
import com.google.inject.persist.RequiresUnitOfWork; | ||
|
||
public class RequiresUnitOfWorkTest extends TestCase { | ||
private Injector injector; | ||
|
||
public void setUp() { | ||
injector = Guice.createInjector(new JpaPersistModule("testUnit")); | ||
|
||
//startup persistence and end the unit of work, so that no one is running | ||
JpaPersistService persistService = injector.getInstance(JpaPersistService.class); | ||
persistService.start(); | ||
persistService.end(); | ||
} | ||
|
||
public void tearDown() { | ||
injector.getInstance(EntityManagerFactory.class).close(); | ||
} | ||
|
||
public void testRequiresWithNoUnitOfWork() { | ||
JpaPersistService persistService = injector.getInstance(JpaPersistService.class); | ||
DataAccessObject dataObject = injector.getInstance(DataAccessObject.class); | ||
|
||
// Run operation with no exception | ||
dataObject.runOperationInUnitOfWork(); | ||
|
||
// Unit of work has been closed | ||
assertFalse(persistService.isWorking()); | ||
} | ||
|
||
public void testRequiresWithUnitOfWork() { | ||
JpaPersistService persistService = injector.getInstance(JpaPersistService.class); | ||
DataAccessObject dataObject = injector.getInstance(DataAccessObject.class); | ||
|
||
// Run operation with no exception | ||
persistService.begin(); | ||
dataObject.runOperationInUnitOfWork(); | ||
|
||
// Unit of work has not been closed | ||
assertTrue(persistService.isWorking()); | ||
persistService.end(); | ||
} | ||
|
||
public void testRequiresWithNestedUnitsOfWork() { | ||
JpaPersistService persistService = injector.getInstance(JpaPersistService.class); | ||
NestDataObject dataObject = injector.getInstance(NestDataObject.class); | ||
|
||
// Run operation with no exception | ||
dataObject.runOperationInUnitOfWork(); | ||
|
||
// Unit of work has not been closed | ||
assertFalse(persistService.isWorking()); | ||
} | ||
|
||
|
||
public static class DataAccessObject { | ||
@Inject Provider<EntityManager> emProvider; | ||
|
||
@RequiresUnitOfWork | ||
public void runOperationInUnitOfWork() { | ||
emProvider.get() | ||
.createQuery("from JpaTestEntity", JpaTestEntity.class) | ||
.setMaxResults(1).getResultList(); | ||
} | ||
} | ||
|
||
public static class NestDataObject { | ||
@Inject Provider<EntityManager> emProvider; | ||
@Inject DataAccessObject dataObject; | ||
|
||
@RequiresUnitOfWork | ||
public void runOperationInUnitOfWork() { | ||
emProvider.get() | ||
.createQuery("from JpaTestEntity", JpaTestEntity.class) | ||
.setMaxResults(1).getResultList(); | ||
} | ||
} | ||
} |