1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/* 2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2011 The Android Open Source Project 3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License. 6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at 7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software 11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and 14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License. 15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal; 18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 19a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.internal.io.FileSystem; 20c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.Closeable; 21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.EOFException; 22c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.File; 23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.FileNotFoundException; 24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException; 25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayList; 26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays; 27c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Iterator; 28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.LinkedHashMap; 29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.NoSuchElementException; 30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.Executor; 31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.concurrent.LinkedBlockingQueue; 32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.concurrent.ThreadPoolExecutor; 33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.concurrent.TimeUnit; 34faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.regex.Matcher; 35faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.regex.Pattern; 36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink; 383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSource; 393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio; 40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Sink; 41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Source; 42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Timeout; 4354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/** 45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * A cache that uses a bounded amount of space on a filesystem. Each cache 46faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * entry has a string key and a fixed number of values. Each key must match 47faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * the regex <strong>[a-z0-9_-]{1,64}</strong>. Values are byte sequences, 48faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * accessible as streams or files. Each value must be between {@code 0} and 49faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * {@code Integer.MAX_VALUE} bytes in length. 50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The cache stores its data in a directory on the filesystem. This 52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * directory must be exclusive to the cache; the cache may delete or overwrite 53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * files from its directory. It is an error for multiple processes to use the 54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * same cache directory at the same time. 55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This cache limits the number of bytes that it will store on the 57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * filesystem. When the number of stored bytes exceeds the limit, the cache will 58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * remove entries in the background until the limit is satisfied. The limit is 59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * not strict: the cache may temporarily exceed it while waiting for files to be 60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * deleted. The limit does not include filesystem overhead or the cache 61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * journal so space-sensitive applications should set a conservative limit. 62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>Clients call {@link #edit} to create or update the values of an entry. An 64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * entry may have only one editor at one time; if a value is not available to be 65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * edited then {@link #edit} will return null. 66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ul> 67faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <li>When an entry is being <strong>created</strong> it is necessary to 68faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * supply a full set of values; the empty value should be used as a 69faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * placeholder if necessary. 70faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <li>When an entry is being <strong>edited</strong>, it is not necessary 71faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * to supply data for every value; values default to their previous 72faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * value. 73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ul> 74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Every {@link #edit} call must be matched by a call to {@link Editor#commit} 75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * or {@link Editor#abort}. Committing is atomic: a read observes the full set 76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * of values as they were before or after the commit, but never a mix of values. 77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>Clients call {@link #get} to read a snapshot of an entry. The read will 79c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * observe the value at the time that {@link #get} was called. Updates and 80c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * removals after the call do not impact ongoing reads. 81c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 82c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This class is tolerant of some I/O errors. If files are missing from the 83c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * filesystem, the corresponding entries will be dropped from the cache. If 84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * an error occurs while writing a cache value, the edit will fail silently. 85c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Callers should handle other problems by catching {@code IOException} and 86c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * responding appropriately. 87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 88c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic final class DiskLruCache implements Closeable { 8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson static final String JOURNAL_FILE = "journal"; 90faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath static final String JOURNAL_FILE_TEMP = "journal.tmp"; 91faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath static final String JOURNAL_FILE_BACKUP = "journal.bkp"; 9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson static final String MAGIC = "libcore.io.DiskLruCache"; 9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson static final String VERSION_1 = "1"; 9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson static final long ANY_SEQUENCE_NUMBER = -1; 95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,120}"); 9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private static final String CLEAN = "CLEAN"; 9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private static final String DIRTY = "DIRTY"; 9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private static final String REMOVE = "REMOVE"; 9954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private static final String READ = "READ"; 10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 101faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /* 102faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * This cache uses a journal file named "journal". A typical journal file 103faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * looks like this: 104faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * libcore.io.DiskLruCache 105faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 1 106faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 100 107faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 2 108faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 109faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 110faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * DIRTY 335c4c6028171cfddfbaae1a9c313c52 111faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 112faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * REMOVE 335c4c6028171cfddfbaae1a9c313c52 113faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * DIRTY 1ab96a171faeeee38496d8b330771a7a 114faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 115faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * READ 335c4c6028171cfddfbaae1a9c313c52 116faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 117faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 118faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * The first five lines of the journal form its header. They are the 119faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * constant string "libcore.io.DiskLruCache", the disk cache's version, 120faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * the application's version, the value count, and a blank line. 121faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 122faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Each of the subsequent lines in the file is a record of the state of a 123faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * cache entry. Each line contains space-separated values: a state, a key, 124faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * and optional state-specific values. 125faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * o DIRTY lines track that an entry is actively being created or updated. 126faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Every successful DIRTY action should be followed by a CLEAN or REMOVE 127faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * action. DIRTY lines without a matching CLEAN or REMOVE indicate that 128faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * temporary files may need to be deleted. 129faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * o CLEAN lines track a cache entry that has been successfully published 130faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * and may be read. A publish line is followed by the lengths of each of 131faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * its values. 132faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * o READ lines track accesses for LRU. 133faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * o REMOVE lines track entries that have been deleted. 134faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 135faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * The journal file is appended to as cache operations occur. The journal may 136faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * occasionally be compacted by dropping redundant lines. A temporary file named 137faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * "journal.tmp" will be used during compaction; that file should be deleted if 138faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * it exists when the cache is opened. 139faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private final FileSystem fileSystem; 14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final File directory; 14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final File journalFile; 14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final File journalFileTmp; 145faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private final File journalFileBackup; 14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final int appVersion; 147faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private long maxSize; 14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final int valueCount; 14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private long size = 0; 1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private BufferedSink journalWriter; 151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<>(0, 0.75f, true); 15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private int redundantOpCount; 153a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private boolean hasJournalErrors; 15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Must be read and written when synchronized on 'this'. 156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean initialized; 157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean closed; 158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * To differentiate between old and current snapshots, each entry is given 16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * a sequence number each time an edit is committed. A snapshot is stale if 16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * its sequence number is not equal to its entry's sequence number. 16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private long nextSequenceNumber = 0; 16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Used to run 'cleanupRunnable' for journal rebuilds. */ 167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final Executor executor; 1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final Runnable cleanupRunnable = new Runnable() { 1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public void run() { 17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson synchronized (DiskLruCache.this) { 171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!initialized | closed) { 172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return; // Nothing to do 173c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller trimToSize(); 1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (journalRebuildRequired()) { 1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller rebuildJournal(); 1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller redundantOpCount = 0; 1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException e) { 1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new RuntimeException(e); 182c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson }; 18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 187a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller DiskLruCache(FileSystem fileSystem, File directory, int appVersion, int valueCount, long maxSize, 188a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Executor executor) { 189a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller this.fileSystem = fileSystem; 19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.directory = directory; 19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.appVersion = appVersion; 19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.journalFile = new File(directory, JOURNAL_FILE); 193faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP); 194faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP); 19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.valueCount = valueCount; 19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.maxSize = maxSize; 197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller this.executor = executor; 19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 20071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller public synchronized void initialize() throws IOException { 201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assert Thread.holdsLock(this); 202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (initialized) { 204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return; // Already initialized. 20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 206c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 207faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // If a bkp file exists, use it instead. 208a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (fileSystem.exists(journalFileBackup)) { 209faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // If journal file also exists just delete backup file. 210a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (fileSystem.exists(journalFile)) { 211a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(journalFileBackup); 212faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 213a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.rename(journalFileBackup, journalFile); 214faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 215faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 216faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 217faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // Prefer to pick up where we left off. 218a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (fileSystem.exists(journalFile)) { 21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readJournal(); 221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller processJournal(); 222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialized = true; 223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return; 22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (IOException journalIsCorrupt) { 225faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath Platform.get().logW("DiskLruCache " + directory + " is corrupt: " 226faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath + journalIsCorrupt.getMessage() + ", removing"); 227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller delete(); 228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller closed = false; 22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 230c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 231c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller rebuildJournal(); 233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialized = true; 235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Create a cache which will reside in {@code directory}. This cache is lazily initialized on 239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * first access and will be created if it does not exist. 240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * @param directory a writable directory 242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * @param valueCount the number of values per cache entry. Must be positive. 243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * @param maxSize the maximum number of bytes this cache should use to store 244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 245a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller public static DiskLruCache create(FileSystem fileSystem, File directory, int appVersion, 246a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller int valueCount, long maxSize) { 247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (maxSize <= 0) { 248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IllegalArgumentException("maxSize <= 0"); 249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (valueCount <= 0) { 251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IllegalArgumentException("valueCount <= 0"); 252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Use a single background thread to evict entries. 255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, 256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp DiskLruCache", true)); 257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 258a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return new DiskLruCache(fileSystem, directory, appVersion, valueCount, maxSize, executor); 25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void readJournal() throws IOException { 262a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller BufferedSource source = Okio.buffer(fileSystem.source(journalFile)); 26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 264c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller String magic = source.readUtf8LineStrict(); 265c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller String version = source.readUtf8LineStrict(); 266c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller String appVersionString = source.readUtf8LineStrict(); 267c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller String valueCountString = source.readUtf8LineStrict(); 268c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller String blank = source.readUtf8LineStrict(); 269faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (!MAGIC.equals(magic) 270faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || !VERSION_1.equals(version) 271faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || !Integer.toString(appVersion).equals(appVersionString) 272faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || !Integer.toString(valueCount).equals(valueCountString) 273faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || !"".equals(blank)) { 274faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " 275faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath + valueCountString + ", " + blank + "]"); 27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 278faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int lineCount = 0; 27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson while (true) { 280c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath try { 281c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller readJournalLine(source.readUtf8LineStrict()); 282faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath lineCount++; 28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (EOFException endOfJournal) { 28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson break; 285c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 287faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath redundantOpCount = lineCount - lruEntries.size(); 288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // If we ended on a truncated line, rebuild the journal before appending to it. 290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!source.exhausted()) { 291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller rebuildJournal(); 292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } else { 293a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller journalWriter = newJournalWriter(); 294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } finally { 2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Util.closeQuietly(source); 297c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 299c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 300a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private BufferedSink newJournalWriter() throws FileNotFoundException { 301a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Sink fileSink = fileSystem.appendingSink(journalFile); 302a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Sink faultHidingSink = new FaultHidingSink(fileSink) { 303a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override protected void onException(IOException e) { 304a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assert (Thread.holdsLock(DiskLruCache.this)); 305a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller hasJournalErrors = true; 306a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 307a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller }; 308a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return Okio.buffer(faultHidingSink); 309a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 310a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void readJournalLine(String line) throws IOException { 312faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int firstSpace = line.indexOf(' '); 313faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (firstSpace == -1) { 31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IOException("unexpected journal line: " + line); 315c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 316c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 317faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int keyBegin = firstSpace + 1; 318faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int secondSpace = line.indexOf(' ', keyBegin); 319faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath final String key; 320faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (secondSpace == -1) { 321faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath key = line.substring(keyBegin); 322faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) { 323faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath lruEntries.remove(key); 324faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return; 325faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 326faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 327faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath key = line.substring(keyBegin, secondSpace); 328c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 329c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Entry entry = lruEntries.get(key); 33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (entry == null) { 33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry = new Entry(key); 33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson lruEntries.put(key, entry); 33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 335c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 336faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) { 337faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath String[] parts = line.substring(secondSpace + 1).split(" "); 33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.readable = true; 33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.currentEditor = null; 340faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath entry.setLengths(parts); 341faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) { 34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.currentEditor = new Editor(entry); 343faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) { 344faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // This work was already done by calling lruEntries.get(). 34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IOException("unexpected journal line: " + line); 34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Computes the initial size and collects garbage as a part of opening the 35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * cache. Dirty entries are assumed to be inconsistent and will be deleted. 35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void processJournal() throws IOException { 355a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(journalFileTmp); 35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) { 35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Entry entry = i.next(); 35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (entry.currentEditor == null) { 35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (int t = 0; t < valueCount; t++) { 36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson size += entry.lengths[t]; 36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.currentEditor = null; 36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (int t = 0; t < valueCount; t++) { 365a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(entry.cleanFiles[t]); 366a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(entry.dirtyFiles[t]); 367c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson i.remove(); 36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Creates a new journal that omits redundant information. This replaces the 37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * current journal if it exists. 37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private synchronized void rebuildJournal() throws IOException { 37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (journalWriter != null) { 37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson journalWriter.close(); 380c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 381c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 382a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller BufferedSink writer = Okio.buffer(fileSystem.sink(journalFileTmp)); 383faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath try { 384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeUtf8(MAGIC).writeByte('\n'); 385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeUtf8(VERSION_1).writeByte('\n'); 386a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller writer.writeDecimalLong(appVersion).writeByte('\n'); 387a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller writer.writeDecimalLong(valueCount).writeByte('\n'); 388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeByte('\n'); 389faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 390faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (Entry entry : lruEntries.values()) { 391faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (entry.currentEditor != null) { 392e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeUtf8(DIRTY).writeByte(' '); 393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeUtf8(entry.key); 394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeByte('\n'); 395faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeUtf8(CLEAN).writeByte(' '); 397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeUtf8(entry.key); 398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller entry.writeLengths(writer); 399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller writer.writeByte('\n'); 400faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 402faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } finally { 403faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath writer.close(); 404faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 405faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 406a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (fileSystem.exists(journalFile)) { 407a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.rename(journalFile, journalFileBackup); 408c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 409a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.rename(journalFileTmp, journalFile); 410a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(journalFileBackup); 411c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 412a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller journalWriter = newJournalWriter(); 413a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller hasJournalErrors = false; 41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns a snapshot of the entry named {@code key}, or null if it doesn't 41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * exist is not currently readable. If a value is returned, it is moved to 41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * the head of the LRU queue. 42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public synchronized Snapshot get(String key) throws IOException { 422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialize(); 423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson checkNotClosed(); 42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson validateKey(key); 42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Entry entry = lruEntries.get(key); 427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (entry == null || !entry.readable) return null; 428c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Snapshot snapshot = entry.snapshot(); 430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (snapshot == null) return null; 431c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson redundantOpCount++; 433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(READ).writeByte(' ').writeUtf8(key).writeByte('\n'); 43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (journalRebuildRequired()) { 435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller executor.execute(cleanupRunnable); 43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 437c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return snapshot; 43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns an editor for the entry named {@code key}, or null if another 44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * edit is in progress. 44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public Editor edit(String key) throws IOException { 44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return edit(key, ANY_SEQUENCE_NUMBER); 44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { 450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialize(); 451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson checkNotClosed(); 45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson validateKey(key); 45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Entry entry = lruEntries.get(key); 45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null 45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson || entry.sequenceNumber != expectedSequenceNumber)) { 457faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; // Snapshot is stale. 45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 459a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (entry != null && entry.currentEditor != null) { 460faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; // Another edit is in progress. 461c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 462c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 463faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // Flush the journal before creating files to prevent file leaks. 464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(DIRTY).writeByte(' ').writeUtf8(key).writeByte('\n'); 46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson journalWriter.flush(); 466a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 467a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (hasJournalErrors) { 468a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return null; // Don't edit; the journal can't be written. 469a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 470a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 471a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (entry == null) { 472a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller entry = new Entry(key); 473a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller lruEntries.put(key, entry); 474a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 475a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Editor editor = new Editor(entry); 476a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller entry.currentEditor = editor; 47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return editor; 47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Returns the directory where this cache stores its data. */ 48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public File getDirectory() { 48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return directory; 48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the maximum number of bytes that this cache should use to store 48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * its data. 48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 489c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller public synchronized long getMaxSize() { 49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return maxSize; 49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 494faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Changes the maximum number of bytes the cache can store and queues a job 495faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * to trim the existing store, if necessary. 496faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 497faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public synchronized void setMaxSize(long maxSize) { 498faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.maxSize = maxSize; 499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (initialized) { 500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller executor.execute(cleanupRunnable); 501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 502faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 503faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 504faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** 50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the number of bytes currently being used to store the values in 50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * this cache. This may be greater than the max size if a background 50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * deletion is pending. 50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public synchronized long size() throws IOException { 510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialize(); 51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return size; 51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private synchronized void completeEdit(Editor editor, boolean success) throws IOException { 51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Entry entry = editor.entry; 51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (entry.currentEditor != editor) { 51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 518c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 519c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 520faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // If this edit is creating the entry for the first time, every index must have a value. 52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (success && !entry.readable) { 52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (int i = 0; i < valueCount; i++) { 52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!editor.written[i]) { 52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson editor.abort(); 52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException("Newly created entry didn't create value for index " + i); 526c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 527a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (!fileSystem.exists(entry.dirtyFiles[i])) { 52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson editor.abort(); 52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 530c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 533c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (int i = 0; i < valueCount; i++) { 535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller File dirty = entry.dirtyFiles[i]; 53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (success) { 537a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (fileSystem.exists(dirty)) { 538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller File clean = entry.cleanFiles[i]; 539a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.rename(dirty, clean); 54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson long oldLength = entry.lengths[i]; 541a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller long newLength = fileSystem.size(clean); 54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.lengths[i] = newLength; 54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson size = size - oldLength + newLength; 54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 546a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(dirty); 54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 548c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 549c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson redundantOpCount++; 55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.currentEditor = null; 55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (entry.readable | success) { 55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.readable = true; 554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(CLEAN).writeByte(' '); 555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(entry.key); 556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller entry.writeLengths(journalWriter); 557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeByte('\n'); 55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (success) { 55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.sequenceNumber = nextSequenceNumber++; 56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson lruEntries.remove(entry.key); 563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(REMOVE).writeByte(' '); 564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(entry.key); 565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeByte('\n'); 566c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 567faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath journalWriter.flush(); 568c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (size > maxSize || journalRebuildRequired()) { 570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller executor.execute(cleanupRunnable); 57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * We only rebuild the journal when it will halve the size of the journal 57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * and eliminate at least 2000 ops. 57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean journalRebuildRequired() { 57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final int redundantOpCompactThreshold = 2000; 5803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return redundantOpCount >= redundantOpCompactThreshold 581faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath && redundantOpCount >= lruEntries.size(); 58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Drops the entry for {@code key} if it exists and can be removed. If the 586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * entry for {@code key} is currently being edited, that edit will complete 587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * normally but its value will not be stored. 58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * 58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @return true if an entry was removed. 59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public synchronized boolean remove(String key) throws IOException { 592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialize(); 593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson checkNotClosed(); 59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson validateKey(key); 59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Entry entry = lruEntries.get(key); 597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (entry == null) return false; 598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return removeEntry(entry); 599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean removeEntry(Entry entry) throws IOException { 602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (entry.currentEditor != null) { 603757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer entry.currentEditor.detach(); // Prevent the edit from completing normally. 604c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 605c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (int i = 0; i < valueCount; i++) { 607a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.delete(entry.cleanFiles[i]); 60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson size -= entry.lengths[i]; 60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.lengths[i] = 0; 610c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 611c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson redundantOpCount++; 613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller journalWriter.writeUtf8(REMOVE).writeByte(' ').writeUtf8(entry.key).writeByte('\n'); 614e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller lruEntries.remove(entry.key); 615c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 61654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (journalRebuildRequired()) { 617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller executor.execute(cleanupRunnable); 61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 619c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 622c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Returns true if this cache has been closed. */ 624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public synchronized boolean isClosed() { 625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return closed; 62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 627c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private synchronized void checkNotClosed() { 629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isClosed()) { 63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException("cache is closed"); 63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Force buffered operations to the filesystem. */ 63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public synchronized void flush() throws IOException { 636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!initialized) return; 637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson checkNotClosed(); 63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson trimToSize(); 64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson journalWriter.flush(); 64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Closes this cache. Stored values will remain on the filesystem. */ 64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public synchronized void close() throws IOException { 645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!initialized || closed) { 646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller closed = true; 647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return; 64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Copying for safe iteration. 650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (Entry entry : lruEntries.values().toArray(new Entry[lruEntries.size()])) { 65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (entry.currentEditor != null) { 65254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson entry.currentEditor.abort(); 65354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 65454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson trimToSize(); 65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson journalWriter.close(); 65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson journalWriter = null; 658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller closed = true; 65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void trimToSize() throws IOException { 66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson while (size > maxSize) { 663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Entry toEvict = lruEntries.values().iterator().next(); 664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller removeEntry(toEvict); 66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Closes the cache and deletes all of its stored values. This will delete 67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * all files in the cache directory including files that weren't created by 67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * the cache. 67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void delete() throws IOException { 67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson close(); 675a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fileSystem.deleteContents(directory); 67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Deletes all stored values from the cache. In-flight edits will complete 680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * normally but their values will not be stored. 681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 682e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public synchronized void evictAll() throws IOException { 683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialize(); 684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Copying for safe iteration. 685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (Entry entry : lruEntries.values().toArray(new Entry[lruEntries.size()])) { 686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller removeEntry(entry); 687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void validateKey(String key) { 691faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath Matcher matcher = LEGAL_KEY_PATTERN.matcher(key); 692faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (!matcher.matches()) { 693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IllegalArgumentException( 694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller "keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\""); 69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 69654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 69754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Returns an iterator over the cache's current entries. This iterator doesn't throw {@code 700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * ConcurrentModificationException}, but if new entries are added while iterating, those new 701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * entries will not be returned by the iterator. If existing entries are removed during iteration, 702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * they will be absent (unless they were already returned). 703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <p>If there are I/O problems during iteration, this iterator fails silently. For example, if 705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * the hosting filesystem becomes unreachable, the iterator will omit elements rather than 706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * throwing exceptions. 707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <p><strong>The caller must {@link Snapshot#close close}</strong> each snapshot returned by 709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * {@link Iterator#next}. Failing to do so leaks open files! 710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <p>The returned iterator supports {@link Iterator#remove}. 712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public synchronized Iterator<Snapshot> snapshots() throws IOException { 714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initialize(); 715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Iterator<Snapshot>() { 716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Iterate a copy of the entries to defend against concurrent modification errors. */ 717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller final Iterator<Entry> delegate = new ArrayList<>(lruEntries.values()).iterator(); 718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** The snapshot to return from {@link #next}. Null if we haven't computed that yet. */ 720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Snapshot nextSnapshot; 721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** The snapshot to remove with {@link #remove}. Null if removal is illegal. */ 723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Snapshot removeSnapshot; 724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public boolean hasNext() { 726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (nextSnapshot != null) return true; 727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller synchronized (DiskLruCache.this) { 729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // If the cache is closed, truncate the iterator. 730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (closed) return false; 731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller while (delegate.hasNext()) { 733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Entry entry = delegate.next(); 734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Snapshot snapshot = entry.snapshot(); 735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (snapshot == null) continue; // Evicted since we copied the entries. 736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller nextSnapshot = snapshot; 737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return true; 738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 742e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Snapshot next() { 745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!hasNext()) throw new NoSuchElementException(); 746e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller removeSnapshot = nextSnapshot; 747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller nextSnapshot = null; 748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return removeSnapshot; 749e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void remove() { 752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (removeSnapshot == null) throw new IllegalStateException("remove() before next()"); 753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller DiskLruCache.this.remove(removeSnapshot.key); 755e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (IOException ignored) { 756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Nothing useful to do here. We failed to remove from the cache. Most likely that's 757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // because we couldn't update the journal, but the cached entry will still be gone. 758e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } finally { 759e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller removeSnapshot = null; 760e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 762e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller }; 76354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 76454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 76554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** A snapshot of the values for an entry. */ 76654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final class Snapshot implements Closeable { 76754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final String key; 76854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final long sequenceNumber; 769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final Source[] sources; 770faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private final long[] lengths; 77154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private Snapshot(String key, long sequenceNumber, Source[] sources, long[] lengths) { 77354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.key = key; 77454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.sequenceNumber = sequenceNumber; 775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller this.sources = sources; 776faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.lengths = lengths; 777c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 778c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 779e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public String key() { 780e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return key; 781e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 783c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 78454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns an editor for this snapshot's entry, or null if either the 78554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * entry has changed since this snapshot was created or if another edit 78654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * is in progress. 787c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 78854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public Editor edit() throws IOException { 78954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return DiskLruCache.this.edit(key, sequenceNumber); 790c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 791c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 79254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Returns the unbuffered stream with the value for {@code index}. */ 793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public Source getSource(int index) { 794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return sources[index]; 79554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 796c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 797faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** Returns the byte length of the value for {@code index}. */ 798faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public long getLength(int index) { 799faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return lengths[index]; 800faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 801faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 802faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public void close() { 803e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (Source in : sources) { 80454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(in); 80554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 80654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 80754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 808c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 809e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private static final Sink NULL_SINK = new Sink() { 810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void write(Buffer source, long byteCount) throws IOException { 811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.skip(byteCount); 812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void flush() throws IOException { 815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 818e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return Timeout.NONE; 819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void close() throws IOException { 822faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 823faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath }; 824faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 82554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Edits the values for an entry. */ 82654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final class Editor { 82754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final Entry entry; 82854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final boolean[] written; 829757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer private boolean done; 830c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 83154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Editor(Entry entry) { 83254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.entry = entry; 83354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.written = (entry.readable) ? null : new boolean[valueCount]; 834c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 835c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 836c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 837757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer * Prevents this editor from completing normally. This is necessary either when the edit causes 838757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer * an I/O error, or if the target entry is evicted while this editor is active. In either case 839757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer * we delete the editor's created files and prevent new files from being created. Note that once 840757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer * an editor has been detached it is possible for another editor to edit the entry. 841757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer */ 842757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer void detach() { 843757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (entry.currentEditor == this) { 844757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer for (int i = 0; i < valueCount; i++) { 845757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer try { 846757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer fileSystem.delete(entry.dirtyFiles[i]); 847757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } catch (IOException e) { 848757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer // This file is potentially leaked. Not much we can do about that. 849757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 850757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 851757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer entry.currentEditor = null; 852757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 853757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 854757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer 855757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer /** 85654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns an unbuffered input stream to read the last committed value, 85754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * or null if no value has been committed. 858c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public Source newSource(int index) throws IOException { 86054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson synchronized (DiskLruCache.this) { 861757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (done) { 86254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 863c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 864757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (!entry.readable || entry.currentEditor != this) { 86554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 86654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 867faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath try { 868a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return fileSystem.source(entry.cleanFiles[index]); 869faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } catch (FileNotFoundException e) { 870faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; 871faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 87254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 873c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 874c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 875c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 87654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns a new unbuffered output stream to write the value at 87754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * {@code index}. If the underlying output stream encounters errors 87854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * when writing to the filesystem, this edit will be aborted when 87954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * {@link #commit} is called. The returned output stream does not throw 88054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * IOExceptions. 881c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public Sink newSink(int index) throws IOException { 88354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson synchronized (DiskLruCache.this) { 884757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (done) { 88554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 886c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 887757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (entry.currentEditor != this) { 888757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer return NULL_SINK; 889757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 89054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!entry.readable) { 89154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson written[index] = true; 892c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller File dirtyFile = entry.dirtyFiles[index]; 894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Sink sink; 895faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath try { 896a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink = fileSystem.sink(dirtyFile); 897faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } catch (FileNotFoundException e) { 898a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return NULL_SINK; 899faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 900a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return new FaultHidingSink(sink) { 901a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override protected void onException(IOException e) { 902a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller synchronized (DiskLruCache.this) { 903757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer detach(); 904a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 905a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 906a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller }; 90754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 908c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 909c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 910c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 91154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Commits this edit so it is visible to readers. This releases the 91254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * edit lock so another edit may be started on the same key. 913c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 91454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void commit() throws IOException { 915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller synchronized (DiskLruCache.this) { 916757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (done) { 917757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer throw new IllegalStateException(); 918757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 919757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (entry.currentEditor == this) { 920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller completeEdit(this, true); 921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 922757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer done = true; 92354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 924c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 925c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 926c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 92754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Aborts this edit. This releases the edit lock so another edit may be 92854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * started on the same key. 929c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 93054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void abort() throws IOException { 931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller synchronized (DiskLruCache.this) { 932757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (done) { 933757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer throw new IllegalStateException(); 934757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 935757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (entry.currentEditor == this) { 936757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer completeEdit(this, false); 937757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer } 938757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer done = true; 939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 940c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 941c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 942faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public void abortUnlessCommitted() { 943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller synchronized (DiskLruCache.this) { 944757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer if (!done && entry.currentEditor == this) { 945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller completeEdit(this, false); 947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (IOException ignored) { 948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 949faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 950faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 951faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 95254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 953c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 95454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final class Entry { 95554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final String key; 956c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 95754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Lengths of this entry's files. */ 95854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private final long[] lengths; 959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final File[] cleanFiles; 960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final File[] dirtyFiles; 961c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 96254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** True if this entry has ever been published. */ 96354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean readable; 964c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 96554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** The ongoing edit or null if this entry is not being edited. */ 96654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Editor currentEditor; 967c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 96854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** The sequence number of the most recently committed edit to this entry. */ 96954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private long sequenceNumber; 970c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 97154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Entry(String key) { 97254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.key = key; 973c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller lengths = new long[valueCount]; 975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller cleanFiles = new File[valueCount]; 976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller dirtyFiles = new File[valueCount]; 977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // The names are repetitive so re-use the same builder to avoid allocations. 979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller StringBuilder fileBuilder = new StringBuilder(key).append('.'); 980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int truncateTo = fileBuilder.length(); 981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int i = 0; i < valueCount; i++) { 982e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fileBuilder.append(i); 983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller cleanFiles[i] = new File(directory, fileBuilder.toString()); 984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fileBuilder.append(".tmp"); 985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller dirtyFiles[i] = new File(directory, fileBuilder.toString()); 986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fileBuilder.setLength(truncateTo); 98754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 98854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 989c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 99054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Set lengths using decimal numbers like "10123". */ 99154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void setLengths(String[] strings) throws IOException { 99254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (strings.length != valueCount) { 99354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw invalidLengths(strings); 99454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 995c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 99654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 99754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson for (int i = 0; i < strings.length; i++) { 99854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson lengths[i] = Long.parseLong(strings[i]); 999c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 100054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (NumberFormatException e) { 100154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw invalidLengths(strings); 100254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 100354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 1004c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 1005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Append space-prefixed lengths to {@code writer}. */ 1006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller void writeLengths(BufferedSink writer) throws IOException { 1007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (long length : lengths) { 1008a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller writer.writeByte(' ').writeDecimalLong(length); 1009e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 101054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 1011c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 1012e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private IOException invalidLengths(String[] strings) throws IOException { 1013e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IOException("unexpected journal line: " + Arrays.toString(strings)); 101454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 101554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 1017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Returns a snapshot of this entry. This opens all streams eagerly to guarantee that we see a 1018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * single published snapshot. If we opened streams lazily then the streams could come from 1019e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * different edits. 1020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 1021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Snapshot snapshot() { 1022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!Thread.holdsLock(DiskLruCache.this)) throw new AssertionError(); 1023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 1024e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Source[] sources = new Source[valueCount]; 1025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long[] lengths = this.lengths.clone(); // Defensive copy since these can be zeroed out. 1026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 1027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int i = 0; i < valueCount; i++) { 1028a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sources[i] = fileSystem.source(cleanFiles[i]); 1029e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 1030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Snapshot(key, sequenceNumber, sources, lengths); 1031e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (FileNotFoundException e) { 1032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // A file must have been deleted manually! 1033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int i = 0; i < valueCount; i++) { 1034e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (sources[i] != null) { 1035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Util.closeQuietly(sources[i]); 1036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } else { 1037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 1038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 1039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 1040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return null; 1041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 1042c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 104354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 1044c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 1045