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 021package org.granite.tide.spring; 022 023import org.aspectj.lang.ProceedingJoinPoint; 024import org.aspectj.lang.annotation.Around; 025import org.aspectj.lang.annotation.Aspect; 026import org.granite.gravity.Gravity; 027import org.granite.logging.Logger; 028import org.granite.tide.data.DataContext; 029import org.granite.tide.data.DataEnabled; 030import org.granite.tide.data.DataEnabled.PublishMode; 031import org.granite.tide.data.DataUpdatePostprocessor; 032import org.springframework.beans.factory.annotation.Autowired; 033import org.springframework.core.Ordered; 034import org.springframework.transaction.support.TransactionSynchronizationAdapter; 035import org.springframework.transaction.support.TransactionSynchronizationManager; 036 037/** 038 * Spring AOP AspectJ aspect to handle publishing of data changes instead of relying on the default behaviour 039 * This can be used outside of a HTTP Granite context and inside the security/transaction context 040 * 041 * @author William DRAI 042 */ 043@Aspect 044public class TideDataPublishingAspect implements Ordered { 045 046 private static final Logger log = Logger.getLogger(TideDataPublishingAspect.class); 047 048 private int order = 0; 049 private Gravity gravity; 050 private DataUpdatePostprocessor dataUpdatePostprocessor; 051 052 public void setGravity(Gravity gravity) { 053 this.gravity = gravity; 054 } 055 056 @Autowired(required=false) 057 public void setDataUpdatePostprocessor(DataUpdatePostprocessor dataUpdatePostprocessor) { 058 this.dataUpdatePostprocessor = dataUpdatePostprocessor; 059 } 060 061 public int getOrder() { 062 return order; 063 } 064 public void setOrder(int order) { 065 this.order = order; 066 } 067 068 @Around("@within(dataEnabled)") 069 public Object invoke(ProceedingJoinPoint pjp, DataEnabled dataEnabled) throws Throwable { 070 if (dataEnabled == null || !dataEnabled.useInterceptor()) 071 return pjp.proceed(); 072 073 boolean shouldRemoveContextAtEnd = DataContext.get() == null; 074 boolean shouldInitContext = shouldRemoveContextAtEnd || DataContext.isNull(); 075 boolean onCommit = false; 076 077 if (shouldInitContext) { 078 DataContext.init(gravity, dataEnabled.topic(), dataEnabled.params(), dataEnabled.publish()); 079 if (dataUpdatePostprocessor != null) 080 DataContext.get().setDataUpdatePostprocessor(dataUpdatePostprocessor); 081 } 082 083 DataContext.observe(); 084 try { 085 if (dataEnabled.publish().equals(PublishMode.ON_COMMIT) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 086 if (TransactionSynchronizationManager.isSynchronizationActive()) { 087 TransactionSynchronizationManager.registerSynchronization(new DataPublishingTransactionSynchronization(shouldRemoveContextAtEnd)); 088 onCommit = true; 089 } 090 else 091 log.warn("Could not register synchronization for ON_COMMIT publish mode, check that the Spring PlatformTransactionManager supports it"); 092 } 093 094 Object ret = pjp.proceed(); 095 096 DataContext.publish(PublishMode.ON_SUCCESS); 097 return ret; 098 } 099 finally { 100 if (shouldRemoveContextAtEnd && !onCommit) 101 DataContext.remove(); 102 } 103 } 104 105 private static class DataPublishingTransactionSynchronization extends TransactionSynchronizationAdapter { 106 107 private boolean removeContext = false; 108 109 public DataPublishingTransactionSynchronization(boolean removeContext) { 110 this.removeContext = removeContext; 111 } 112 113 @Override 114 public void beforeCommit(boolean readOnly) { 115 if (!readOnly) 116 DataContext.publish(PublishMode.ON_COMMIT); 117 } 118 119 @Override 120 public void beforeCompletion() { 121 if (removeContext) 122 DataContext.remove(); 123 } 124 } 125}