001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022 023package org.granite.tide.spring; 024 025import org.granite.gravity.Gravity; 026import org.granite.logging.Logger; 027import org.granite.tide.data.DataContext; 028import org.granite.tide.data.DataEnabled; 029import org.granite.tide.data.DataEnabled.PublishMode; 030import org.granite.tide.data.DataUpdatePostprocessor; 031import org.granite.tide.data.TideSynchronizationManager; 032import org.granite.util.ThrowableCallable; 033import org.granite.util.TypeUtil; 034import org.springframework.transaction.support.TransactionSynchronizationAdapter; 035import org.springframework.transaction.support.TransactionSynchronizationManager; 036 037import java.util.HashMap; 038import java.util.Map; 039 040/** 041 * Common class to implement data enabled interceptors 042 */ 043public class TideDataPublishingWrapper { 044 045 private static final Logger log = Logger.getLogger(TideDataPublishingWrapper.class); 046 047 private Map<String, TideSynchronizationManager> syncsMap = new HashMap<String, TideSynchronizationManager>(); 048 049 private Gravity gravity; 050 private DataUpdatePostprocessor dataUpdatePostprocessor; 051 052 public void setGravity(Gravity gravity) { 053 this.gravity = gravity; 054 } 055 056 public void setDataUpdatePostprocessor(DataUpdatePostprocessor dataUpdatePostprocessor) { 057 this.dataUpdatePostprocessor = dataUpdatePostprocessor; 058 } 059 060 public TideDataPublishingWrapper() { 061 try { 062 syncsMap.put("org.springframework.orm.hibernate3.SessionHolder", TypeUtil.newInstance("org.granite.tide.spring.Hibernate3SynchronizationManager", TideSynchronizationManager.class)); 063 } 064 catch (Throwable e) { 065 // Hibernate 3 not present 066 } 067 try { 068 syncsMap.put("org.springframework.orm.hibernate4.SessionHolder", TypeUtil.newInstance("org.granite.tide.spring.Hibernate4SynchronizationManager", TideSynchronizationManager.class)); 069 } 070 catch (Throwable e) { 071 // Hibernate 4 not present 072 } 073 try { 074 syncsMap.put("org.springframework.orm.jpa.EntityManagerHolder", TypeUtil.newInstance("org.granite.tide.spring.JPASynchronizationManager", TideSynchronizationManager.class)); 075 } 076 catch (Throwable e) { 077 // JPA not present 078 } 079 } 080 081 public TideDataPublishingWrapper(Gravity gravity, DataUpdatePostprocessor dataUpdatePostprocessor) { 082 this(); 083 this.gravity = gravity; 084 this.dataUpdatePostprocessor = dataUpdatePostprocessor; 085 } 086 087 public <T> T execute(DataEnabled dataEnabled, ThrowableCallable<T> action) throws Throwable { 088 boolean shouldRemoveContextAtEnd = DataContext.get() == null; 089 boolean shouldInitContext = shouldRemoveContextAtEnd || DataContext.isNull(); 090 boolean onCommit = false; 091 092 if (shouldInitContext) { 093 DataContext.init(gravity, dataEnabled.topic(), dataEnabled.params(), dataEnabled.publish()); 094 if (dataUpdatePostprocessor != null) 095 DataContext.get().setDataUpdatePostprocessor(dataUpdatePostprocessor); 096 } 097 098 DataContext.observe(); 099 try { 100 if (dataEnabled.publish().equals(PublishMode.ON_COMMIT) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 101 if (TransactionSynchronizationManager.isSynchronizationActive()) { 102 boolean registered = false; 103 for (Object resource : TransactionSynchronizationManager.getResourceMap().values()) { 104 if (syncsMap.containsKey(resource.getClass().getName())) { 105 registered = syncsMap.get(resource.getClass().getName()).registerSynchronization(resource, shouldRemoveContextAtEnd); 106 break; 107 } 108 } 109 if (!registered) 110 TransactionSynchronizationManager.registerSynchronization(new DataPublishingTransactionSynchronization(shouldRemoveContextAtEnd)); 111 else if (shouldRemoveContextAtEnd) 112 TransactionSynchronizationManager.registerSynchronization(new DataContextCleanupTransactionSynchronization()); 113 onCommit = true; 114 } 115 else if (TransactionSynchronizationManager.isActualTransactionActive()) { 116 log.error("Could not register synchronization for ON_COMMIT publish mode, check that the Spring PlatformTransactionManager supports it " 117 + "and that the order of the TransactionInterceptor is lower than the order of TideDataPublishingInterceptor"); 118 if (shouldRemoveContextAtEnd) 119 DataContext.remove(); 120 } 121 else { 122 // No transaction, clear data context and wait for a possible manual transactions 123 if (shouldRemoveContextAtEnd) 124 DataContext.remove(); 125 } 126 } 127 128 T ret = action.call(); 129 130 DataContext.publish(PublishMode.ON_SUCCESS); 131 return ret; 132 } 133 finally { 134 if (shouldRemoveContextAtEnd && !onCommit) 135 DataContext.remove(); 136 } 137 } 138 139 private static class DataPublishingTransactionSynchronization extends TransactionSynchronizationAdapter { 140 141 private boolean removeContext = false; 142 143 public DataPublishingTransactionSynchronization(boolean removeContext) { 144 this.removeContext = removeContext; 145 } 146 147 @Override 148 public void beforeCommit(boolean readOnly) { 149 if (!readOnly) 150 DataContext.publish(PublishMode.ON_COMMIT); 151 } 152 153 @Override 154 public void beforeCompletion() { 155 if (removeContext) 156 DataContext.remove(); 157 } 158 159 @Override 160 public void afterCompletion(int status) { 161 if (removeContext) 162 DataContext.remove(); 163 } 164 } 165 166 private static class DataContextCleanupTransactionSynchronization extends TransactionSynchronizationAdapter { 167 168 @Override 169 public void afterCompletion(int status) { 170 DataContext.remove(); 171 } 172 } 173 174}