001package systems.dmx.core.impl;
002
003import systems.dmx.core.service.Transactional;
004import systems.dmx.core.storage.spi.DMXTransaction;
005
006import com.sun.jersey.api.model.AbstractMethod;
007import com.sun.jersey.spi.container.ContainerRequest;
008import com.sun.jersey.spi.container.ContainerRequestFilter;
009import com.sun.jersey.spi.container.ContainerResponse;
010import com.sun.jersey.spi.container.ContainerResponseFilter;
011import com.sun.jersey.spi.container.ResourceFilter;
012import com.sun.jersey.spi.container.ResourceFilterFactory;
013
014import java.lang.reflect.Method;
015import java.util.ArrayList;
016import java.util.List;
017import java.util.logging.Logger;
018
019
020
021class TransactionFactory implements ResourceFilterFactory {
022
023    // ---------------------------------------------------------------------------------------------- Instance Variables
024
025    private PersistenceLayer pl;
026
027    private Logger logger = Logger.getLogger(getClass().getName());
028
029    // ------------------------------------------------------------------------------------------------- Class Variables
030
031    private static final ThreadLocal<DMXTransaction> threadLocalTransaction = new ThreadLocal();
032
033    // ---------------------------------------------------------------------------------------------------- Constructors
034
035    TransactionFactory(PersistenceLayer pl) {
036        this.pl = pl;
037    }
038
039    // -------------------------------------------------------------------------------------------------- Public Methods
040
041    @Override
042    public List<ResourceFilter> create(AbstractMethod method) {
043        if (!method.isAnnotationPresent(Transactional.class)) {
044            return null;
045        }
046        //
047        logger.fine("### Adding transaction support to " + info(method));
048        List<ResourceFilter> filters = new ArrayList();
049        filters.add(new TransactionResourceFilter(method));
050        return filters;
051    }
052
053    // ------------------------------------------------------------------------------------------------- Private Methods
054
055    private String info(AbstractMethod method) {
056        Method m = method.getMethod();
057        return m.getDeclaringClass().getName() + "#" + m.getName() + "()";
058    }
059
060    // ------------------------------------------------------------------------------------------------- Private Classes
061
062    private class TransactionResourceFilter implements ResourceFilter {
063
064        private AbstractMethod method;
065
066        private TransactionResourceFilter(AbstractMethod method) {
067            this.method = method;
068        }
069
070        @Override
071        public ContainerRequestFilter getRequestFilter() {
072            return new ContainerRequestFilter() {
073
074                @Override
075                public ContainerRequest filter(ContainerRequest request) {
076                    logger.fine("### Begining transaction of " + info(method));
077                    DMXTransaction tx = pl.beginTx();
078                    threadLocalTransaction.set(tx);
079                    return request;
080                }
081            };
082        }
083
084        @Override
085        public ContainerResponseFilter getResponseFilter() {
086            return new ContainerResponseFilter() {
087
088                @Override
089                public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
090                    DMXTransaction tx = threadLocalTransaction.get();
091                    boolean success = response.getMappedThrowable() == null;    // ### TODO: is this criteria concise?
092                    if (success) {
093                        logger.fine("### Comitting transaction of " + info(method));
094                        tx.success();
095                    } else {
096                        logger.warning("### Rollback transaction of " + info(method));
097                    }
098                    tx.finish();
099                    return response;
100                }
101            };
102        }
103    }
104}