001/*
002 * Copyright 2009 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 */
016package org.kuali.ole.sys.batch.service;
017
018import java.util.HashMap;
019import java.util.LinkedHashMap;
020import java.util.Map;
021
022import org.kuali.ole.gl.businessobject.AccountBalance;
023import org.kuali.rice.krad.bo.BusinessObject;
024import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
025import 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
032public 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        
048        protected LinkedHashMap toStringMapper_RICE20_REFACTORME() {
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}