org.sonar.home.cache.FileCache.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.home.cache.FileCache.java

Source

/*
 * Sonar, open source software quality management tool.
 * Copyright (C) 2008-2012 SonarSource
 * mailto:contact AT sonarsource DOT com
 *
 * Sonar is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Sonar is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sonar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.home.cache;

import org.apache.commons.io.FileUtils;
import org.sonar.home.log.Log;

import javax.annotation.CheckForNull;

import java.io.File;
import java.io.IOException;
import java.util.Random;

/**
 * This class is responsible for managing Sonar batch file cache. You can put file into cache and
 * later try to retrieve them. MD5 is used to differentiate files (name is not secure as files may come
 * from different Sonar servers and have same name but be actually different, and same for SNAPSHOTs).
 */
public class FileCache {

    private static final int TEMP_FILE_ATTEMPTS = 1000;

    private final File dir, tmpDir;
    private final FileHashes hashes;
    private final Log log;

    FileCache(File dir, Log log, FileHashes fileHashes) {
        this.hashes = fileHashes;
        this.log = log;
        this.dir = createDir(dir, log, "user cache");
        log.info(String.format("User cache: %s", dir.getAbsolutePath()));
        this.tmpDir = createDir(new File(dir, "_tmp"), log, "temp dir");
    }

    public static FileCache create(File dir, Log log) {
        return new FileCache(dir, log, new FileHashes());
    }

    public File getDir() {
        return dir;
    }

    /**
     * Look for a file in the cache by its filename and md5 checksum. If the file is not
     * present then return null.
     */
    @CheckForNull
    public File get(String filename, String hash) {
        File cachedFile = new File(new File(dir, hash), filename);
        if (cachedFile.exists()) {
            return cachedFile;
        }
        log.debug(String.format("No file found in the cache with name %s and hash %s", filename, hash));
        return null;
    }

    public interface Downloader {
        void download(String filename, File toFile) throws IOException;
    }

    public File get(String filename, String hash, Downloader downloader) {
        // Does not fail if another process tries to create the directory at the same time.
        File hashDir = hashDir(hash);
        File targetFile = new File(hashDir, filename);
        if (!targetFile.exists()) {
            File tempFile = newTempFile();
            download(downloader, filename, tempFile);
            String downloadedHash = hashes.of(tempFile);
            if (!hash.equals(downloadedHash)) {
                throw new IllegalStateException("INVALID HASH: File " + tempFile.getAbsolutePath()
                        + " was expected to have hash " + hash + " but was downloaded with hash " + downloadedHash);
            }
            mkdirQuietly(hashDir);
            renameQuietly(tempFile, targetFile);
        }
        return targetFile;
    }

    private void download(Downloader downloader, String filename, File tempFile) {
        try {
            downloader.download(filename, tempFile);
        } catch (IOException e) {
            throw new IllegalStateException("Fail to download " + filename + " to " + tempFile, e);
        }
    }

    private void renameQuietly(File sourceFile, File targetFile) {
        boolean rename = sourceFile.renameTo(targetFile);
        // Check if the file was cached by another process during download
        if (!rename && !targetFile.exists()) {
            log.warn(String.format("Unable to rename %s to %s", sourceFile.getAbsolutePath(),
                    targetFile.getAbsolutePath()));
            log.warn(String.format("A copy/delete will be tempted but with no garantee of atomicity"));
            try {
                FileUtils.moveFile(sourceFile, targetFile);
            } catch (IOException e) {
                throw new IllegalStateException(
                        "Fail to move " + sourceFile.getAbsolutePath() + " to " + targetFile, e);
            }
        }
    }

    private File hashDir(String hash) {
        return new File(dir, hash);
    }

    private void mkdirQuietly(File hashDir) {
        try {
            FileUtils.forceMkdir(hashDir);
        } catch (IOException e) {
            throw new IllegalStateException("Fail to create cache directory: " + hashDir, e);
        }
    }

    private File newTempFile() {
        String baseName = System.currentTimeMillis() + "-";
        Random random = new Random();
        for (int counter = 0; counter < TEMP_FILE_ATTEMPTS; counter++) {
            try {
                String filename = baseName + random.nextInt(1000);
                File tempFile = new File(tmpDir, filename);
                if (tempFile.createNewFile()) {
                    return tempFile;
                }
            } catch (IOException e) {
                // ignore except the last try
                if (counter == TEMP_FILE_ATTEMPTS - 1) {
                    throw new IllegalStateException();
                }
            }
        }
        throw new IllegalStateException("Fail to create temporary file in " + tmpDir);
    }

    private File createDir(File dir, Log log, String debugTitle) {
        if (!dir.isDirectory() || !dir.exists()) {
            log.debug("Create : " + dir.getAbsolutePath());
            try {
                FileUtils.forceMkdir(dir);
            } catch (IOException e) {
                throw new IllegalStateException("Unable to create " + debugTitle + dir.getAbsolutePath(), e);
            }
        }
        return dir;
    }

}