001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.domain;
022
023import org.biojava.nbio.structure.align.util.AtomCache;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027import java.io.*;
028import java.util.HashMap;
029import java.util.Map;
030
031
032/** A class that provides all that is necessary to create a Serializable Cache
033 *
034 * @author Andreas Prlic
035 *
036 * @param <K> The key type of the cache
037 * @param <V> the value type to be stored on the cache
038 *
039 * @since 3.0.3
040 */
041public class SerializableCache <K,V>{
042
043        private static final Logger logger = LoggerFactory.getLogger(SerializableCache.class);
044
045        protected String cacheFileName;
046        protected Map<K,V> serializedCache ;
047
048
049        /** set cacheFileName to null to disable caching
050         *
051         * @param cacheFileName
052         */
053        public SerializableCache(String cacheFileName ) {
054                this.cacheFileName = cacheFileName;
055
056                if ( cacheFileName != null) {
057                        reloadFromFile();
058                }
059
060        }
061
062        public boolean isCacheEnabled(){
063                return ( serializedCache != null );
064        }
065
066        /** This will not cache null values.
067         *  Null means not cached yet.
068         *  If you want to cache "no data exists" use e.g. empty collections to represent this.
069         *
070         * @param name
071         * @param data
072         */
073        public void cache(K name, V data) {
074
075                if ( data == null){
076                        return;
077                }
078                if ( serializedCache != null){
079
080
081                        logger.debug("Caching {}  {}", name, data);
082
083                        serializedCache.put(name,data);
084
085
086                        // every 1000 objects we are writing to disk
087                        if ( serializedCache.keySet().size() % 1000 == 0 ) {
088
089                                flushCache();
090
091                        }
092
093                }
094
095
096        }
097
098        public V get(K name) {
099                if ( serializedCache == null)
100                        return null;
101                return (serializedCache.get(name));
102        }
103
104        public void disableCache(){
105                //flushCache();
106                serializedCache = null;
107        }
108
109        public void enableCache(){
110                reloadFromFile();
111        }
112
113
114
115        @SuppressWarnings("unchecked")
116        public Map<K,V> reloadFromFile() {
117
118                File f = getCacheFile();
119
120                serializedCache = new HashMap<K,V>();
121
122                // has never been cached here before
123                if( ! f.exists()) {
124                        logger.info("Creating new cache " + f.getAbsolutePath());
125                        return serializedCache;
126                }
127
128                try{
129
130                        logger.debug("Reloading from cache " + f.getAbsolutePath());
131
132                        FileInputStream fis = new FileInputStream(f);
133                        ObjectInputStream ois = new ObjectInputStream(fis);
134                        serializedCache = (HashMap<K,V>) ois.readObject();
135                        ois.close();
136                } catch (IOException e){
137                        // TODO shouldn't this be thrown forward?
138                        logger.error("Exception caught while reading serialized file",e);
139                        return null;
140                } catch (ClassNotFoundException e) {
141                        logger.error("Exception caught while reading serialized file",e);
142                        return null;
143                }
144
145                //if ( debug )
146                logger.info("Reloaded from cache: " + f.getName()+ " size: " + serializedCache.keySet().size() + " cached records.");
147                return serializedCache;
148        }
149
150        private File getCacheFile() {
151                AtomCache cache =new AtomCache();
152                String path = cache.getCachePath();
153                File f = new File(path + System.getProperty("file.separator") + cacheFileName);
154
155                logger.debug(f.getAbsolutePath());
156                return f;
157        }
158
159        public void flushCache(){
160                if ( serializedCache == null)
161                        return;
162                synchronized(serializedCache){
163
164                        File f = getCacheFile();
165
166                        try {
167
168                                FileOutputStream fos = new FileOutputStream(f);
169                                ObjectOutputStream oos = new ObjectOutputStream(fos);
170                                oos.writeObject(serializedCache);
171                                oos.close();
172                        } catch (IOException e){
173                                logger.error("Exception caught", e);
174                        }
175                }
176        }
177
178}