package org.ovirt.engine.core.utils.archivers.tar;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Handles cache tar file based on directory.
 *
 * Cache tar based on directory structure. If files are changed
 * recreate tar file. Test file change once per interval.
 */
public class CachedTar {

    private static final Logger log = LoggerFactory.getLogger(CachedTar.class);

    private long refreshInterval = 10000;
    private long nextCheckTime = 0;

    private File archive;
    private File dir;

    private void create(long timestamp) throws IOException {
        // must create within same directory
        // so rename be atomic
        File temp = File.createTempFile(
            this.archive.getName(),
            "tmp",
            this.archive.getParentFile()
        );
        try {
            try (OutputStream os = new FileOutputStream(temp)) {
                Tar.doTar(os, this.dir);
            }
            catch(IOException e) {
                throw new IOException(String.format("Cannot create tarball '%1$s'", this.archive), e);
            }

            if (!temp.setLastModified(timestamp)) {
                throw new IOException(
                    String.format(
                        "Cannot set last modified '%1$s' to '%2$d'",
                        temp.getCanonicalPath(),
                        timestamp
                    )
                );
            }

            if (!temp.renameTo(this.archive)) {
                throw new IOException(
                    String.format(
                        "Cannot rename '%1$s' to '%2$s'",
                        temp.getCanonicalPath(),
                        archive.getCanonicalPath()
                    )
                );
            }

            temp = null;
        }
        catch(IOException e) {
            log.error("Exception", e);
            throw e;
        }
        finally {
            if (temp != null && !temp.delete()) {
                log.error("Cannot delete '{}'", temp.getAbsolutePath());
            }
        }
    }

    private void ensure() throws IOException {
        if (!this.archive.exists()) {
            log.info(
                "Tarball '{}' is missing, creating",
                this.archive.getAbsolutePath()
            );
            this.nextCheckTime = System.currentTimeMillis() + this.refreshInterval;
            create(getTimestampRecursive(this.dir));
        }
        else if (this.nextCheckTime <= System.currentTimeMillis()) {
            this.nextCheckTime = System.currentTimeMillis() + this.refreshInterval;

            long treeTimestamp = getTimestampRecursive(this.dir);
            if (archive.lastModified() != treeTimestamp) {
                log.info(
                    "Tarball '{}' is out of date, re-creating",
                    this.archive.getAbsolutePath()
                );
                create(treeTimestamp);
            }
        }
    }

    /**
     * Constructor.
     * @param archive name of tar to cache.
     * @param dir base directory.
     */
    public CachedTar(File archive, File dir) {
        this.archive = archive;
        this.dir = dir;

        this.refreshInterval = Config.<Integer>getValue(
            ConfigValues.BootstrapCacheRefreshInterval
        );
    }

    /**
     * Get file of archive, without enforcing cache.
     * This should be used only if file is not to be accessed (messages).
     * @return File name.
     */
    public File getFileNoUse() {
        return this.archive;
    }

    /**
     * Get file of archive.
     * @return File name.
     */
    public File getFile() throws IOException {
        ensure();
        return this.archive;
    }

    /**
     * Returns the maximum timestamp of directory tree.
     *
     * @param file
     *            directory/file name.
     * @return max timestamp.
     */
    private static long getTimestampRecursive(File file) {
        if (file.isDirectory()) {
            long m = 0;
            for (String name : file.list()) {
                m = Math.max(m, getTimestampRecursive(new File(file, name)));
            }
            return m;
        }
        else if (file.isFile()) {
            return file.lastModified();
        }
        else {
            return 0;
        }
    }
}

@@ -765,22 +765,8 @@
 ate(
-long timestamp
 ) th
@@ -1299,342 +1299,8 @@
  %7D%0A%0A
-            if (!temp.setLastModified(timestamp)) %7B%0A                throw new IOException(%0A                    String.format(%0A                        %22Cannot set last modified '%251$s' to '%252$d'%22,%0A                        temp.getCanonicalPath(),%0A                        timestamp%0A                    )%0A                );%0A            %7D%0A%0A
     
@@ -2020,16 +2020,68 @@
 exists()
+ %7C%7C this.nextCheckTime %3C= System.currentTimeMillis()
 ) %7B%0A    
@@ -2132,28 +2132,15 @@
 %7B%7D' 
-is missing, creating
+refresh
 %22,%0A 
@@ -2307,543 +2307,10 @@
 ate(
-getTimestampRecursive(this.dir));%0A        %7D%0A        else if (this.nextCheckTime %3C= System.currentTimeMillis()) %7B%0A            this.nextCheckTime = System.currentTimeMillis() + this.refreshInterval;%0A%0A            long treeTimestamp = getTimestampRecursive(this.dir);%0A            if (archive.lastModified() != treeTimestamp) %7B%0A                log.info(%0A                    %22Tarball '%7B%7D' is out of date, re-creating%22,%0A                    this.archive.getAbsolutePath()%0A                );%0A                create(treeTimestamp);%0A            %7D
+);
 %0A   
@@ -3082,599 +3082,7 @@
    %7D
-%0A%0A    /**%0A     * Returns the maximum timestamp of directory tree.%0A     *%0A     * @param file%0A     *            directory/file name.%0A     * @return max timestamp.%0A     */%0A    private static long getTimestampRecursive(File file) %7B%0A        if (file.isDirectory()) %7B%0A            long m = 0;%0A            for (String name : file.list()) %7B%0A                m = Math.max(m, getTimestampRecursive(new File(file, name)));%0A            %7D%0A            return m;%0A        %7D%0A        else if (file.isFile()) %7B%0A            return file.lastModified();%0A        %7D%0A        else %7B%0A            return 0;%0A        %7D%0A    %7D
 %0A%7D%0A

remove timestamp cache check for tarball