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 * Created on Oct 1, 2009 021 * Author: Andreas Prlic 022 * 023 */ 024 025package org.biojava.nbio.core.util; 026 027import java.io.ByteArrayInputStream; 028import java.io.File; 029import java.io.FileInputStream; 030import java.io.IOException; 031import java.io.InputStream; 032 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * Provides a cache for storing multiple small files in memory. Can be used to e.g cache gzip compressed PDB files 038 * for avoiding disk IO bottlenecks. 039 * Note this is just a wrapper for the singleton cache. 040 * 041 * @author Andreas Prlic. 042 * 043 */ 044public class FlatFileCache { 045 046 private final static Logger logger = LoggerFactory.getLogger(FlatFileCache.class); 047 048 /** 049 * The cache singleton. 050 */ 051 private static SoftHashMap<String, byte[]> cache = new SoftHashMap<String, byte[]>(0); 052 053 054 // no public constructor; 055 private FlatFileCache(){ 056 057 } 058 059 /** 060 * The file is read and the bytes stored immediately. 061 * <p/> 062 * Once added, {@code fileToCache} can be modified or deleted and the cached values will not change. 063 * @param key 064 * @param fileToCache A readable file, of Integer.MAX bytes length or less. 065 */ 066 public static void addToCache(String key, File fileToCache){ 067 //logger.debug("storing " + key + " on file cache (cache size: " + cache.size() + ")"); 068 try (InputStream is = new FileInputStream(fileToCache)){ 069 070 // Get the size of the file 071 long length = fileToCache.length(); 072 073 // You cannot create an array using a long type. 074 // It needs to be an int type. 075 // Before converting to an int type, check 076 // to ensure that file is not larger than Integer.MAX_VALUE. 077 if (length > Integer.MAX_VALUE) { 078 // File is too large 079 throw new IllegalArgumentException("File must be <= " + Integer.MAX_VALUE + " bytes long"); 080 } 081 082 // Create the byte array to hold the data 083 byte[] bytes = new byte[(int)length]; 084 085 // Read in the bytes 086 int offset = 0; 087 int numRead = 0; 088 while (offset < bytes.length 089 && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { 090 offset += numRead; 091 } 092 093 // Ensure all the bytes have been read in 094 if (offset < bytes.length) { 095 is.close(); 096 throw new IOException("Could not completely read file "+fileToCache.getName()); 097 } 098 099 // Close the input stream and return bytes 100 is.close(); 101 102 cache.put(key,bytes); 103 104 } catch (Exception e){ 105 logger.error("Error adding to cache! " + e.getMessage(), e); 106 } 107 } 108 /** 109 * Gets the cached file as an InputStream. 110 * Clients should check for null as the item might have expired in the cache. 111 * @param key 112 * @return An {@code InputStream} or null. 113 */ 114 public static InputStream getInputStream(String key){ 115 //logger.debug("returning " + key + " from file cache (cache size: " + cache.size() + ")"); 116 byte[] bytes = cache.get(key); 117 if ( bytes == null) 118 return null; 119 120 return new ByteArrayInputStream(bytes); 121 122 } 123 124 /** 125 * Returns the number of items in the cache. 126 * If the cache is {@}, returns -1 127 * @return 128 */ 129 public static int size() { 130 if ( cache != null) 131 return cache.size(); 132 else 133 return -1; 134 } 135 136 /** 137 * Removes all elements from the cache 138 */ 139 public static void clear(){ 140 cache.clear(); 141 } 142 143}