001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.tide.ejb;
022    
023    import javax.interceptor.AroundInvoke;
024    import javax.interceptor.Interceptor;
025    import javax.interceptor.InvocationContext;
026    import javax.naming.InitialContext;
027    import javax.transaction.Synchronization;
028    import javax.transaction.TransactionSynchronizationRegistry;
029    
030    import org.granite.tide.data.DataContext;
031    import org.granite.tide.data.DataEnabled;
032    import org.granite.tide.data.DataEnabled.PublishMode;
033    import org.granite.tide.data.JMSDataDispatcher;
034    
035    
036    /**
037     * EJB interceptor to handle publishing of data changes instead of relying on the default behaviour
038     * This can be used outside of a HTTP Granite context and inside the security/transaction context
039     * @author William DRAI
040     *
041     */
042    @Interceptor
043    public class TideDataPublishingInterceptor {
044            
045            @AroundInvoke
046        public Object processPublishData(InvocationContext invocationContext) throws Exception {
047            if (invocationContext.getMethod() == null) {
048                    // Lifecycle method
049                    return invocationContext.proceed();
050            }
051            
052            DataEnabled dataEnabled = invocationContext.getTarget().getClass().getAnnotation(DataEnabled.class);
053            if (dataEnabled == null || !dataEnabled.useInterceptor())
054                    return invocationContext.proceed();
055            
056            boolean shouldRemoveContextAtEnd = DataContext.get() == null;
057            boolean shouldInitContext = shouldRemoveContextAtEnd || DataContext.isNull();
058            boolean onCommit = false;
059            
060            if (shouldInitContext)
061                    DataContext.init(new JMSDataDispatcher(dataEnabled.topic(), onCommit, dataEnabled.params()), dataEnabled.publish());
062            
063            DataContext.observe();
064            try {
065                    if (dataEnabled.publish().equals(PublishMode.ON_COMMIT)) {
066                            InitialContext ctx = new InitialContext();
067                            TransactionSynchronizationRegistry tsr = (TransactionSynchronizationRegistry)ctx.lookup("java:comp/TransactionSynchronizationRegistry");
068                            tsr.registerInterposedSynchronization(new DataPublishingSynchronization(shouldRemoveContextAtEnd));
069                            onCommit = true;
070                    }
071                    
072                    Object ret = invocationContext.proceed();
073                    
074                    DataContext.publish(PublishMode.ON_SUCCESS);
075                    return ret;
076            }
077            finally {
078                    if (shouldRemoveContextAtEnd && !onCommit)
079                            DataContext.remove();
080            }
081        }
082        
083        private static class DataPublishingSynchronization implements Synchronization {
084            
085            private final boolean removeContext;
086            
087            public DataPublishingSynchronization(boolean removeContext) {
088                    this.removeContext = removeContext;
089            }
090    
091                    public void beforeCompletion() {
092                            DataContext.publish(PublishMode.ON_COMMIT);
093                            if (removeContext)
094                                    DataContext.remove();
095                    }
096    
097                    public void afterCompletion(int status) {
098                            if (removeContext)
099                                    DataContext.remove();
100                    }
101            
102        }
103    }