Skip to content

Instantly share code, notes, and snippets.

@picodotdev
Last active December 27, 2015 04:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save picodotdev/7264990 to your computer and use it in GitHub Desktop.
Save picodotdev/7264990 to your computer and use it in GitHub Desktop.
package es.com.blogspot.elblogdepicodev.plugintapestry.services;
...
public class AppModule {
public static void bind(ServiceBinder binder) {
...
// Servicios para la gestión de transacciones
binder.bind(HibernateSessionManager.class, HibernateSessionManagerImpl.class).scope(ScopeConstants.PERTHREAD).withId("AppHibernateSessionManager");
binder.bind(TransactionAdvisor.class, TransactionAdvisorImpl.class);
binder.bind(TransactionService.class, HibernateTransactionServiceImpl.class).scope(ScopeConstants.PERTHREAD);
...
}
public static void contributeServiceOverride(MappedConfiguration<Class,Object> configuration, @Local HibernateSessionManager sessionManager) {
configuration.add(HibernateSessionManager.class, sessionManager);
}
...
/**
* Dar soporte transaccional a los servicios con una interfaz que cumplan el patrón (los advices se aplican a los métodos de una interfaz).
*/
@Match({ "*DAO" })
public static void adviseTransaction(TransactionAdvisor advisor, MethodAdviceReceiver receiver) {
advisor.addAdvice(receiver);
}
}
$ git clone git://github.com/picodotdev/elblogdepicodev.git
$ cd elblogdepicodev/PlugInTapestry
$ ./gradlew tomcatRun
# Abrir en el navegador http://localhost:8080/PlugInTapestry/
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import org.apache.tapestry5.hibernate.HibernateSessionManager;
import org.apache.tapestry5.hibernate.HibernateSessionSource;
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.hibernate.Session;
public class HibernateSessionManagerImpl implements HibernateSessionManager {
private Session session;
public HibernateSessionManagerImpl(HibernateSessionSource source, PerthreadManager manager) {
this.session = source.create();
manager.addThreadCleanupCallback(new Runnable() {
@Override
public void run() {
cleanup();
}
});
}
public void abort() {
session.getTransaction().rollback();
}
public void commit() {
session.getTransaction().commit();
}
public Session getSession() {
return session;
}
private void cleanup() {
session.close();
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Stack;
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.jdbc.Work;
public class HibernateTransactionServiceImpl implements TransactionService {
private Session session;
private Stack<Transaction> transactionStack;
public HibernateTransactionServiceImpl(Session session, PerthreadManager perthreadManager) {
this.session = session;
this.transactionStack = new Stack<Transaction>();
perthreadManager.addThreadCleanupCallback(new Runnable() {
@Override
public void run() {
cleanup();
}
});
}
public boolean beginIfNoPresent(TransactionDefinition definition) {
if (isWithinTransaction()) {
return false;
}
begin(definition);
return true;
}
public void begin(TransactionDefinition definition) {
Transaction transaction = session.beginTransaction();
configure(session, transaction, definition);
transactionStack.push(transaction);
}
public void commit() {
if (isWithinTransaction()) {
transactionStack.pop().commit();
}
}
public void rollback() {
if (isWithinTransaction()) {
transactionStack.pop().rollback();
}
}
public boolean isWithinTransaction() {
return !transactionStack.empty();
}
private void cleanup() {
for (Transaction transaction : transactionStack) {
transaction.rollback();
}
}
private void configure(Session session, Transaction transaction, final TransactionDefinition definition) {
if (definition.getReadOnly() != null) {
session.setDefaultReadOnly(definition.getReadOnly());
}
if (definition.getTimeout() != null) {
transaction.setTimeout(definition.getTimeout());
}
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
if (definition.getReadOnly() != null) {
connection.setReadOnly(definition.getReadOnly());
}
if (definition.getIsolation() != null) {
connection.setTransactionIsolation(definition.getIsolation().intValue());
}
}
});
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
public class MandatoryTransactionAdvice implements MethodAdvice {
private TransactionService service;
public MandatoryTransactionAdvice(TransactionService service) {
this.service = service;
}
public void advise(MethodInvocation invocation) {
if (!service.isWithinTransaction()) {
throw new RuntimeException("Debe haber una transacción");
}
invocation.proceed();
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
public class NestedTransactionAdvice implements MethodAdvice {
private TransactionDefinition definition;
private TransactionService service;
public NestedTransactionAdvice(TransactionDefinition definition, TransactionService service) {
this.definition = definition;
this.service = service;
}
public void advise(MethodInvocation invocation) {
try {
service.begin(definition);
invocation.proceed();
service.commit();
} catch (Exception e) {
service.rollback();
throw e;
}
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
public class NeverTransactionAdvice implements MethodAdvice {
private TransactionService service;
public NeverTransactionAdvice(TransactionService service) {
this.service = service;
}
public void advise(MethodInvocation invocation) {
if (service.isWithinTransaction()) {
throw new RuntimeException("Hay una transacción activa y se require ninguna");
}
invocation.proceed();
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
public enum Propagation {
REQUIRED, SUPPORTS, NEVER, NESTED, MANDATORY
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
public class RequiredTransactionAdvice implements MethodAdvice {
private TransactionDefinition definition;
private TransactionService service;
public RequiredTransactionAdvice(TransactionDefinition definition, TransactionService service) {
this.definition = definition;
this.service = service;
}
public void advise(MethodInvocation invocation) {
boolean isNew = service.beginIfNoPresent(definition);
try {
invocation.proceed();
if (isNew) {
service.commit();
}
} catch (Exception e) {
if (isNew) {
service.rollback();
}
throw e;
}
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import org.apache.tapestry5.ioc.MethodAdviceReceiver;
public interface TransactionAdvisor {
void addAdvice(MethodAdviceReceiver methodAdviceReceiver);
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import java.lang.reflect.Method;
import org.apache.tapestry5.ioc.MethodAdviceReceiver;
public class TransactionAdvisorImpl implements TransactionAdvisor {
private TransactionService service;
public TransactionAdvisorImpl(TransactionService service) {
this.service = service;
}
public void addAdvice(final MethodAdviceReceiver receiver) {
for (Method method : receiver.getInterface().getMethods()) {
Transactional transactional = method.getAnnotation(Transactional.class);
if (transactional != null) {
adviceMethod(buildTransactionDefinition(transactional), method, receiver);
}
}
}
private void adviceMethod(TransactionDefinition definition, Method method, MethodAdviceReceiver receiver) {
switch (definition.getPropagation()) {
case REQUIRED:
receiver.adviseMethod(method, new RequiredTransactionAdvice(definition, service));
break;
case NESTED:
receiver.adviseMethod(method, new NestedTransactionAdvice(definition, service));
break;
case MANDATORY:
receiver.adviseMethod(method, new MandatoryTransactionAdvice(service));
break;
case NEVER:
receiver.adviseMethod(method, new NeverTransactionAdvice(service));
break;
case SUPPORTS:
break;
}
}
private TransactionDefinition buildTransactionDefinition(Transactional transactional) {
return new TransactionDefinition(transactional.propagation(), (transactional.isolation() == -1) ? null : transactional.isolation(), transactional.readonly(),
(transactional.timeout() == -1) ? null : transactional.timeout());
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface Transactional {
Propagation propagation() default Propagation.REQUIRED;
int isolation() default -1;
boolean readonly() default false;
int timeout() default -1;
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
public class TransactionDefinition {
private Propagation propagation;
private Integer isolation;
private Boolean readOnly;
private Integer timeout;
public TransactionDefinition(Propagation propagation, Integer isolation, Boolean readOnly, Integer timeout) {
this.propagation = propagation;
this.isolation = isolation;
this.readOnly = readOnly;
this.timeout = timeout;
}
public Propagation getPropagation() {
return propagation;
}
public void setPropagation(Propagation propagation) {
this.propagation = propagation;
}
public Integer getIsolation() {
return isolation;
}
public void setIsolation(Integer isolation) {
this.isolation = isolation;
}
public Boolean getReadOnly() {
return readOnly;
}
public void setReadOnly(Boolean readOnly) {
this.readOnly = readOnly;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
}
package es.com.blogspot.elblogdepicodev.plugintapestry.services.transaction;
public interface TransactionService {
boolean beginIfNoPresent(TransactionDefinition definition);
void begin(TransactionDefinition definition);
void commit();
void rollback();
boolean isWithinTransaction();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment