* 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;

  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
  public synchronized DiskStorage get() throws IOException {
    if (shouldCreateNewStorage()) {
      // discard anything we created
    return Preconditions.checkNotNull(mCurrentState.storage);

  private boolean shouldCreateNewStorage() {
    State currentState = mCurrentState;
    return (currentState.storage == null ||
        currentState.rootDirectory == null ||

  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

  private void createStorage() throws IOException {
    DiskStorage storage = new DefaultDiskStorage(mRootDirectory, mVersion, mCacheErrorLogger);
    mCurrentState = new State(mRootDirectory, storage);

  void createRootDirectoryIfNecessary(File rootDirectory) throws IOException {
    try {
    } catch (FileUtils.CreateDirectoryException cde) {
      throw cde;
    FLog.d(TAG, "Created cache directory %s", rootDirectory.getAbsolutePath());