/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.cache.disk; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import com.facebook.cache.common.CacheErrorLogger; import com.facebook.common.file.FileTree; import com.facebook.common.file.FileUtils; import com.facebook.common.internal.Preconditions; import com.facebook.common.internal.VisibleForTesting; import com.facebook.common.logging.FLog; /** * A supplier of a DiskStorage concrete implementation. */ public class DefaultDiskStorageSupplier implements DiskStorageSupplier { private static final Class<?> TAG = DefaultDiskStorageSupplier.class; private final int mVersion; private final File mRootDirectory; private final CacheErrorLogger mCacheErrorLogger; @VisibleForTesting volatile State mCurrentState; /** * Represents the current 'cached' state. */ @VisibleForTesting static class State { public final @Nullable DiskStorage storage; public final @Nullable File rootDirectory; @VisibleForTesting State(@Nullable File rootDirectory, @Nullable DiskStorage storage) { this.storage = storage; this.rootDirectory = rootDirectory; } } public DefaultDiskStorageSupplier( int version, File baseDirectoryPath, String baseDirectoryName, CacheErrorLogger cacheErrorLogger) { mVersion = version; mCacheErrorLogger = cacheErrorLogger; mRootDirectory = new File(baseDirectoryPath, baseDirectoryName); mCurrentState = new State(null, null); } /** * Gets a concrete disk-storage instance. If nothing has changed since the last call, then * the last state is returned * @return an instance of the appropriate DiskStorage class * @throws IOException */ @Override public synchronized DiskStorage get() throws IOException { if (shouldCreateNewStorage()) { // discard anything we created deleteOldStorageIfNecessary(); createStorage(); } return Preconditions.checkNotNull(mCurrentState.storage); } private boolean shouldCreateNewStorage() { State currentState = mCurrentState; return (currentState.storage == null || currentState.rootDirectory == null || !currentState.rootDirectory.exists()); } @VisibleForTesting void deleteOldStorageIfNecessary() { if (mCurrentState.storage != null && mCurrentState.rootDirectory != null) { // LATER: Actually delegate this call to the storage. We shouldn't be // making an end-run around it FileTree.deleteRecursively(mCurrentState.rootDirectory); } } private void createStorage() throws IOException { createRootDirectoryIfNecessary(mRootDirectory); DiskStorage storage = new DefaultDiskStorage(mRootDirectory, mVersion, mCacheErrorLogger); mCurrentState = new State(mRootDirectory, storage); } @VisibleForTesting void createRootDirectoryIfNecessary(File rootDirectory) throws IOException { try { FileUtils.mkdirs(rootDirectory); } catch (FileUtils.CreateDirectoryException cde) { mCacheErrorLogger.logError( CacheErrorLogger.CacheErrorCategory.WRITE_CREATE_DIR, TAG, "createRootDirectoryIfNecessary", cde); throw cde; } FLog.d(TAG, "Created cache directory %s", rootDirectory.getAbsolutePath()); } }