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 }