Skip to content
This repository has been archived by the owner on May 12, 2020. It is now read-only.

Add a shutdown hook to delete extracted files #141

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,77 @@
import de.flapdoodle.embed.process.config.IRuntimeConfig;
import de.flapdoodle.embed.process.distribution.Distribution;
import de.flapdoodle.embed.process.extract.IExtractedFileSet;
import de.flapdoodle.embed.process.runtime.ProcessControl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.qatools.embed.postgresql.config.PostgresConfig;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;

/**
* postgres executable
*/
public class PostgresExecutable extends AbstractPGExecutable<PostgresConfig, PostgresProcess> {
final IRuntimeConfig runtimeConfig;

public PostgresExecutable(Distribution distribution,
PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet exe) {
super(distribution, config, runtimeConfig, exe);
this.runtimeConfig = runtimeConfig;
}

@Override
protected PostgresProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime)
throws IOException {
addShutdownHook(runtime, distribution);
return new PostgresProcess(distribution, config, runtime, this);
}
}

private void addShutdownHook(IRuntimeConfig runtimeConfig, Distribution distribution) throws IOException {
ProcessControl.addShutdownHook(
new CleanerRunner(runtimeConfig.getArtifactStore().extractFileSet(distribution).baseDir())
);
}

static class CleanerRunner implements Runnable {

private File fileOrDirectory;

CleanerRunner(File fileOrDirectory) {
this.fileOrDirectory = fileOrDirectory;
}

// TODO インスタンス生成するのはここで良いのか?もっと違うやり方があるのでは?
DirectoryCleaner getCleaner() {
return new DirectoryCleaner();
}

@Override
public void run() {
getCleaner().clean(this.fileOrDirectory);
}
}

static class DirectoryCleaner {
private static Logger logger = LoggerFactory.getLogger(DirectoryCleaner.class);

void clean(File cleanupTarget) {
synchronized (DirectoryCleaner.class) {
if (!cleanupTarget.exists()) {
return;
}

try (Stream<Path> stream = Files.walk(cleanupTarget.toPath())) {
stream.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.filter(File::exists)
.forEach(File::delete);
} catch (IOException e) {
logger.warn("Could not delete {}.", cleanupTarget.getAbsolutePath(), e);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ru.yandex.qatools.embed.postgresql;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.UUID;
import java.util.stream.Stream;

import static org.junit.Assert.assertFalse;

@RunWith(MockitoJUnitRunner.class)
public class TestDeleteTemporaryDirectory {

private static final Logger logger = LoggerFactory.getLogger(TestDeleteTemporaryDirectory.class);
private Path testDirectory;

@Before
public void setup() {
testDirectory = Paths.get(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
}

@After
public void cleanup() {
if (!testDirectory.toFile().exists()) {
return;
}

try (Stream<Path> stream = Files.walk(testDirectory)) {
stream.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.filter(File::exists)
.forEach(File::delete);
} catch (IOException e) {
logger.warn("Could not delete {}.", testDirectory.toFile().getAbsolutePath(), e);
}
}

@Test
public void callCleanMethodWhenCleanerRunnerIsRun() {
// Mock DirectoryCleaner
PostgresExecutable.DirectoryCleaner cleaner = Mockito.spy(PostgresExecutable.DirectoryCleaner.class);
Mockito.doNothing().when(cleaner).clean(Mockito.any(File.class));

// Run CleanerRunner
PostgresExecutable.CleanerRunner runner =
Mockito.spy(new PostgresExecutable.CleanerRunner(testDirectory.toFile()));
Mockito.doReturn(cleaner).when(runner).getCleaner();
runner.run();

// Clean method was called by CleanerRunner
Mockito.verify(cleaner, Mockito.times(1)).clean(testDirectory.toFile());
}

@Test
public void directoryCleanerDeleteFilesInDirectoryAndItself() throws IOException {
createTestDirectories(testDirectory);

// Run clean method
new PostgresExecutable.DirectoryCleaner().clean(testDirectory.toFile());

// Removed test directory
assertFalse("Directory should be removed. " + testDirectory, testDirectory.toFile().exists());
}

private void createTestDirectories(Path root) throws IOException {
// foo/
// ├── file1
// ├── file2
// ├── bar/
// │   ├── fileA
// │   └── fileB
// └── baz/
Path foo = Paths.get(root.toFile().getAbsolutePath(), "foo");
Path bar = Paths.get(foo.toFile().getAbsolutePath(), "bar");
Path baz = Paths.get(foo.toFile().getAbsolutePath(), "baz");
Files.createDirectories(foo);
Files.createDirectories(bar);
Files.createDirectories(baz);

Files.createFile(Paths.get(foo.toFile().getAbsolutePath(), "file1"));
Files.createFile(Paths.get(foo.toFile().getAbsolutePath(), "file2"));
Files.createFile(Paths.get(bar.toFile().getAbsolutePath(), "fileA"));
Files.createFile(Paths.get(bar.toFile().getAbsolutePath(), "fileB"));
}
}