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    }