/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.sys.batch.service; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.kuali.rice.krad.bo.BusinessObject; import org.kuali.rice.krad.bo.PersistableBusinessObjectBase; import org.kuali.rice.krad.util.ObjectUtils; import org.springframework.transaction.annotation.Transactional; /** * This class CANNOT be used by 2 processes simultaneously. It is for very specific batch processes that should not run at the same * time, and initialize and destroy must be called and the beginning and end of each process that uses it. */ @Transactional public abstract class AbstractBatchTransactionalCachingService implements WrappingBatchService { private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AbstractBatchTransactionalCachingService.class); protected Map<String,BusinessObject> referenceValueCache; protected Map<Class,PreviousValueReference> previousValueCache; public void initialize() { referenceValueCache = new HashMap<String,BusinessObject>(); previousValueCache = new HashMap<Class,PreviousValueReference>(); } public void destroy() { referenceValueCache = null; previousValueCache = null; } static final class NonExistentReferenceBusinessObject extends PersistableBusinessObjectBase { protected LinkedHashMap toStringMapper_RICE20_REFACTORME() { throw new UnsupportedOperationException(); } } protected static final BusinessObject NON_EXISTENT_REFERENCE_CACHE_VALUE = new NonExistentReferenceBusinessObject(); protected String getCacheKey(Class clazz, Object...objects) { StringBuffer cacheKey = new StringBuffer(clazz.getName()); for (int i = 0; i < objects.length; i++) { cacheKey.append("-").append(objects[i]); } return cacheKey.toString(); } protected abstract class ReferenceValueRetriever<T extends BusinessObject> { public T get(Class<T> type, Object...keys) { String cacheKey = getCacheKey(type, keys); BusinessObject businessObject = referenceValueCache.get(cacheKey); if (businessObject == null) { try { businessObject = useDao(); } catch (Exception e) { throw new RuntimeException("Unable to getBusinessObject in AccountingCycleCachingServiceImpl: " + cacheKey, e); } if (businessObject == null) { referenceValueCache.put(cacheKey, NON_EXISTENT_REFERENCE_CACHE_VALUE); } else { referenceValueCache.put(cacheKey, businessObject); retrieveReferences((T)businessObject); } } else if (businessObject instanceof NonExistentReferenceBusinessObject) { businessObject = null; } return (T)businessObject; } protected abstract T useDao(); protected abstract void retrieveReferences(T object); } public class PreviousValueReference<T extends BusinessObject> { protected String key = ""; protected T value; public T getValue() { return value; } public void update(T value, String key) { this.key = key; this.value = value; } public void update(T value, Object...keys) { update (value, getCacheKey(value.getClass(), keys)); } } // protected abstract class PreviousValueRetriever<T extends BusinessObject> { // public T get(Class<T> type, Object...keys) { // String cacheKey = getCacheKey(type, keys); // if (!cacheKey.equals(previousValueCache.get(AccountBalance.class).key.equals(cacheKey))) { // previousValueCache.get(type).update(useDao(), cacheKey); // } // return (T)previousValueCache.get(type).getValue(); // } // protected abstract T useDao(); // } protected abstract class PreviousValueRetriever<T extends BusinessObject> { public T get(Class<T> type, Object...keys) { // this should never happen, but in did, so just in case if (ObjectUtils.isNull(previousValueCache)) { LOG.error("previousValueCache is null. This shouldn't have happened."); previousValueCache = new HashMap<Class,PreviousValueReference>(); } PreviousValueReference<T> pvr = previousValueCache.get(type); if (ObjectUtils.isNull(pvr)) { LOG.warn("PreviousValueReference for type " + type + " is not initialized; adding a new one to previousValueCache for it."); pvr = new PreviousValueReference<T>(); previousValueCache.put(type, pvr); } String cacheKey = getCacheKey(type, keys); if (!StringUtils.equals(cacheKey, pvr.key)) { pvr.update(useDao(), cacheKey); } return pvr.getValue(); } protected abstract T useDao(); } }