001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.kfs.sys.batch.service;
017
018 import java.util.HashMap;
019 import java.util.LinkedHashMap;
020 import java.util.Map;
021
022 import org.kuali.kfs.gl.businessobject.AccountBalance;
023 import org.kuali.rice.kns.bo.BusinessObject;
024 import org.kuali.rice.kns.bo.PersistableBusinessObjectBase;
025 import org.springframework.transaction.annotation.Transactional;
026
027 /**
028 * This class CANNOT be used by 2 processes simultaneously. It is for very specific batch processes that should not run at the same
029 * time, and initialize and destroy must be called and the beginning and end of each process that uses it.
030 */
031 @Transactional
032 public abstract class AbstractBatchTransactionalCachingService implements WrappingBatchService {
033 protected Map<String,BusinessObject> referenceValueCache;
034 protected Map<Class,PreviousValueReference> previousValueCache;
035
036 public void initialize() {
037 referenceValueCache = new HashMap<String,BusinessObject>();
038 previousValueCache = new HashMap<Class,PreviousValueReference>();
039 }
040
041 public void destroy() {
042 referenceValueCache = null;
043 previousValueCache = null;
044 }
045
046 static final class NonExistentReferenceBusinessObject extends PersistableBusinessObjectBase {
047 @Override
048 protected LinkedHashMap toStringMapper() {
049 throw new UnsupportedOperationException();
050 }
051 }
052 protected static final BusinessObject NON_EXISTENT_REFERENCE_CACHE_VALUE = new NonExistentReferenceBusinessObject();
053 protected String getCacheKey(Class clazz, Object...objects) {
054 StringBuffer cacheKey = new StringBuffer(clazz.getName());
055 for (int i = 0; i < objects.length; i++) {
056 cacheKey.append("-").append(objects[i]);
057 }
058 return cacheKey.toString();
059 }
060 protected abstract class ReferenceValueRetriever<T extends BusinessObject> {
061 public T get(Class<T> type, Object...keys) {
062 String cacheKey = getCacheKey(type, keys);
063 BusinessObject businessObject = referenceValueCache.get(cacheKey);
064 if (businessObject == null) {
065 try {
066 businessObject = useDao();
067 }
068 catch (Exception e) {
069 throw new RuntimeException("Unable to getBusinessObject in AccountingCycleCachingServiceImpl: " + cacheKey, e);
070 }
071 if (businessObject == null) {
072 referenceValueCache.put(cacheKey, NON_EXISTENT_REFERENCE_CACHE_VALUE);
073 }
074 else {
075 referenceValueCache.put(cacheKey, businessObject);
076 retrieveReferences((T)businessObject);
077 }
078 }
079 else if (businessObject instanceof NonExistentReferenceBusinessObject) {
080 businessObject = null;
081 }
082 return (T)businessObject;
083 }
084 protected abstract T useDao();
085 protected abstract void retrieveReferences(T object);
086 }
087 public class PreviousValueReference<T extends BusinessObject> {
088 protected String key = "";
089 protected T value;
090 public T getValue() {
091 return value;
092 }
093 public void update(T value, String key) {
094 this.key = key;
095 this.value = value;
096 }
097 public void update(T value, Object...keys) {
098 update (value, getCacheKey(value.getClass(), keys));
099 }
100 }
101 protected abstract class PreviousValueRetriever<T extends BusinessObject> {
102 public T get(Class<T> type, Object...keys) {
103 String cacheKey = getCacheKey(type, keys);
104 if (!cacheKey.equals(previousValueCache.get(AccountBalance.class).key.equals(cacheKey))) {
105 previousValueCache.get(type).update(useDao(), cacheKey);
106 }
107 return (T)previousValueCache.get(type).getValue();
108 }
109 protected abstract T useDao();
110 }
111 }