No you are not the first to have this problem
https://groups.google.com/d/msg/google-guice/-/eltHSCDwcNwJ
https://groups.google.com/d/msg/google-guice/-/utXFimt1S0cJ

You can use this as a startingpoint:
I copied this from a running project and removed all project specific code.
It's possible that it doesn't compile right a way but it should get you 
started quickly.
this code allows to define custom transaction annotations to start a 
transaction only on a specific data source.

most important for you is proabably the configureServlets() method. From 
how you describe your problem I think this is what is missing in your code.


Once I find some time, i will refactor this whole code and offer it as a 
patch to GuicePersist.

----------


public class MultiDatasourcePersistModule extends ServletModule {

    //---- Fields

    /** The data source this persist module is for. */
    private final DataSource dataSource;


    //---- Constructor

    /**
     * Creates a new Instance.
     *
     * @param dataSource  the data source this perist module is for. Must 
not be {@code null}.
     */
    public MultiDatasourcePersistModule(DataSource dataSource) {
        checkNotNull(dataSource);
        this.dataSource = dataSource;
    }


    //---- Methods

    /**
     * {@inheritDoc}
     */
    @Override
    protected final void configureServlets() {
        final PrivateJpaPersistModule jpm = new PrivateJpaPersistModule();
        install(jpm);

        
bindInterceptor(annotatedWith(dataSource.getTransactionAnnotationType()), 
any(), jpm.getTransactionInterceptor());
        bindInterceptor(any(), 
annotatedWith(dataSource.getTransactionAnnotationType()), 
jpm.getTransactionInterceptor());

        
filter(dataSource.getUrlFilterPattern()).through(jpm.getPersistFilter());
    }


    //---- Inner Class

    /**
     * This private module encapsulates the actual {@link 
JpaPersistModule}. This is necessary because Guice allows only a single 
{@link JpaPersistModule}
     * to be defined in the global context.
     */
    private class PrivateJpaPersistModule extends PrivateModule {

        //---- Fields

        private final Key<PersistFilter> persistFilterKey = 
Key.get(PersistFilter.class, dataSource.getDataSourceAnnotationType());

        private MethodInterceptor transactionInterceptor;


        //---- Methods

        /**
         * {@inheritDoc}
         */
        @Override
        public void configure() {
            final JpaPersistModule jpm = new 
JpaPersistModule(dataSource.getDataSourceName());
            jpm.properties(dataSource.getDataSourceProperties());

            install(jpm);

            final Provider<UnitOfWork> unitOfWorkProvider = 
binder().getProvider(UnitOfWork.class);
            
bind(UnitOfWork.class).annotatedWith(dataSource.getDataSourceAnnotationType()).toProvider(unitOfWorkProvider);
            
expose(UnitOfWork.class).annotatedWith(dataSource.getDataSourceAnnotationType());

            bind(persistFilterKey).to(PersistFilter.class);
            expose(persistFilterKey);

            transactionInterceptor = new TransactionInterceptor(dataSource);
            requestInjection(transactionInterceptor);
        }

        /**
         * @return the key to the persist filter.
         */
        public Key<PersistFilter> getPersistFilter() {
            return persistFilterKey;
        }

        /**
         * Returns an interceptor which will handle all transactions for 
the data source.
         *
         * @return the interceptor.
         */
        public MethodInterceptor getTransactionInterceptor() {
            return transactionInterceptor;
        }
    }


------------


public interface DataSource {

    //---- Methods

    /**
     * @return the name of the data source represented by this instance.
     */
    String getDataSourceName();

    /**
     * @return the properties for the data source represented by this 
instance.
     */
    Properties getDataSourceProperties();

    /**
     * @return the annotation used for marking methods that should be 
executed within a transaction and use the session provider specified by 
this data source.
     */
    Class<? extends Annotation> getTransactionAnnotationType();

    /**
     * @return the session provider for this data source.
     */
    Class<? extends AbstractSessionProvider> getSessionProviderType();

    /**
     * @return the annotation which is used for binding the data source 
provider.
     */
    Class<? extends Annotation> getDataSourceAnnotationType();

    /**
     * @return the URL filter pattern that determines for which requests 
the {@link PersistFilter} is applicable.
     */
    String getUrlFilterPattern();
}


------------


public class TransactionInterceptor implements MethodInterceptor {

    //---- Static

    private static final Logger LOG = 
LoggerFactory.getLogger(TransactionInterceptor.class);

    //---- Fields

    private final Class<? extends Annotation> annotationToIntercept;
    private final String dataSourceName;

    @Inject
    private Provider<EntityManager> emProvider;


    //---- Constructor

    /**
     * Creates a new Instance.
     *
     * @param dataSource  the data source this transaction interceptor is 
for.
     */
    public TransactionInterceptor(DataSource dataSource) {
        this.annotationToIntercept = 
dataSource.getTransactionAnnotationType();
        this.dataSourceName = dataSource.getDataSourceName();
    }


    //---- Methods

    /**
     * {@inheritDoc}
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws 
Throwable {
        // this will start the corresponding UnitOfWork if it has not been 
started yet.
        final EnityManager em = emProvider.get();

        final EntityTransaction txn = em.getTransaction();
        final Transaction transaction = Transaction.get(txn, 
dataSourceName);

        return doTransactional(methodInvocation, transaction, txn);
    }

    private Object doTransactional(MethodInvocation methodInvocation, 
Transaction transaction) throws Throwable {
        final Object result;
        transaction.begin();

        try {
            result = methodInvocation.proceed();
        }
        catch (Exception e) {
            final TransactionalFacade transactional = 
readTransactionMetadata(methodInvocation);

            if (rollbackIsNecessary(transactional, e)) {
                transaction.rollback();
            }
            else {
                transaction.commit();
            }

            throw e;
        }

        transaction.commit();
        return result;
    }

    private TransactionalFacade readTransactionMetadata(MethodInvocation 
methodInvocation) {
        Annotation annotation;
        final Method method = methodInvocation.getMethod();
        final Class<?> targetClass = methodInvocation.getThis().getClass();

        annotation = method.getAnnotation(annotationToIntercept);
        if (null == annotation) {
            // If none on method, try the class.
            annotation = targetClass.getAnnotation(annotationToIntercept);
        }
        if (null == annotation) {
            // If there is no annotation present, use the default
            annotation = Defaults.of(annotationToIntercept);
        }

        return TransactionalFacadeFactory.getFacadeFor(annotation);
    }

    /**
     * Returns True if a rollback is necessary.
     *
     * @param transactional  The metadata annotation of the method
     * @param e              The exception to test for rollback
     * @return {@code true} if a rollback is necessary, {@code false} 
otherwise.
     */
    private boolean rollbackIsNecessary(TransactionalFacade transactional, 
Exception e) {
        boolean rollback = false;

        // check rollback clauses
        for (Class<? extends Exception> rollBackOn : 
transactional.rollbackOn()) {

            // if one matched, try to perform a rollback
            if (rollBackOn.isInstance(e)) {
                rollback = true;

                // check ignore clauses (supercedes rollback clause)
                for (Class<? extends Exception> exceptOn : 
transactional.ignore()) {
                    // An exception to the rollback clause was found, DON'T 
rollback
                    // (i.e. commit and throw anyway)
                    if (exceptOn.isInstance(e)) {
                        rollback = false;
                        break;
                    }
                }

                // done checking once we found a hit
                break;
            }
        }

        return rollback;
    }


    //---- Inner Class

    private abstract static class Transaction {

        protected final EntityTransaction txn;
        protected final String dataSourceName;

        public static Transaction get(EntityTransaction txn, String 
dataSourceName) {
            if (txn.isActive()) {
                return new InnerTransaction(txn, dataSourceName);
            }
            return new OuterTransaction(txn, dataSourceName);
        }

        public Transaction(EntityTransaction txn, String dataSourceName) {
            this.txn = txn;
            this.dataSourceName = dataSourceName;
        }

        public abstract void begin(TransactionImplementor txn);
        public abstract void commit(TransactionImplementor txn);
        public abstract void rollback(TransactionImplementor txn);
    }

    private static class InnerTransaction extends Transaction {
        InnerTransaction(EntityTransaction txn, String dataSourceName) {
            super(txn, dataSourceName);
        }
        @Override
        public void begin() {
            LOG.trace("begin inner transaction ({}) - do nothing", 
dataSourceName);
            // do nothing
        }
        @Override
        public void commit() {
            LOG.trace("commit inner transaction ({}) - do nothing", 
dataSourceName);
            // do nothing
        }

        @Override
        public void rollback() {
            LOG.trace("rollback inner transaction ({}) - mark rollback 
only", dataSourceName);
            txn.markRollbackOnly();
        }
    }

    private static class OuterTransaction extends Transaction {
        OuterTransaction(EntityTransaction txn, String dataSourceName) {
            super(txn, dataSourceName);
        }
        @Override
        public void begin() {
            LOG.trace("begin transaction ({})", dataSourceName);
            txn.begin();
        }
        @Override
        public void commit() {
            LOG.trace("commit transaction ({})", dataSourceName);
            txn.commit();
        }
        @Override
        public void rollback() {
            LOG.trace("rollback transaction ({})", dataSourceName);
            txn.rollback();
        }
    }

    private static final class Defaults implements InvocationHandler {

        // Statics

        /**
         * Returns a dynamic proxy for the given annotation which will 
return the default values for any method invocation.
         *
         * @param annotation  the annotation for which to create a proxy.
         * @return the proxy.
         */
        public static <A extends Annotation> A of(Class<A> annotation) {
            @SuppressWarnings("unchecked")
            final A proxy = (A) 
Proxy.newProxyInstance(annotation.getClassLoader(), new Class[] 
{annotation}, new Defaults());
            return proxy;
        }


        //---- Methods

        /**
         * return the default value of the annotation method
         */
        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
            return method.getDefaultValue();
        }
    }

}


----------


public interface TransactionalFacade {

    /**
     * A list of exceptions to rollback on, if thrown by the transactional 
method.
     * These exceptions are propagated correctly after a rollback.
     */
    public Class<? extends Exception>[] rollbackOn();

    /**
     * A list of exceptions to <b>not<b> rollback on. A caveat to the 
rollbackOn clause.
     * The disjunction of rollbackOn and ignore represents the list of 
exceptions
     * that will trigger a rollback.
     * The complement of rollbackOn and the universal set plus any 
exceptions in the
     * ignore set represents the list of exceptions that will trigger a 
commit.
     * Note that ignore exceptions take precedence over rollbackOn, but 
with subtype
     * granularity.
     */
    public Class<? extends Exception>[] ignore();

}

-- 
You received this message because you are subscribed to the Google Groups 
"google-guice" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/google-guice/-/CO-fhWjs1IcJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-guice?hl=en.

Reply via email to