Skip to content

Commit

Permalink
Fix hanging files at instance destroy
Browse files Browse the repository at this point in the history
  • Loading branch information
Yann Diorcet committed Sep 15, 2015
1 parent f9ceff2 commit e157fdc
Showing 1 changed file with 111 additions and 31 deletions.
142 changes: 111 additions & 31 deletions src/main/java/com/jcabi/mysql/maven/plugin/Instances.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;

Expand Down Expand Up @@ -97,8 +98,8 @@ public final class Instances {
/**
* Running processes.
*/
private final transient ConcurrentMap<Integer, Process> processes =
new ConcurrentHashMap<Integer, Process>(0);
private final transient ConcurrentMap<Integer, DatabaseInstance> processes =
new ConcurrentHashMap<Integer, DatabaseInstance>(0);

/**
* If true, always create a new database. If false, check if there is an
Expand Down Expand Up @@ -127,18 +128,9 @@ public void start(@NotNull final Config config, @NotNull final File dist,
String.format("port %d is already busy", config.port())
);
}
final Process proc = this.process(config, dist, target, socket);
this.processes.put(config.port(), proc);
Runtime.getRuntime().addShutdownHook(
new Thread(
new Runnable() {
@Override
public void run() {
Instances.this.stop(config.port());
}
}
)
);
final DatabaseInstance instance = this.process(config, dist, target, socket);
this.processes.put(config.port(), instance);
instance.addShutdownHook(config.port());
}
}

Expand All @@ -148,9 +140,10 @@ public void run() {
*/
public void stop(final int port) {
synchronized (this.processes) {
final Process proc = this.processes.remove(port);
if (proc != null) {
proc.destroy();
final DatabaseInstance instance = this.processes.remove(port);
if (instance != null) {
instance.removeShutdownHook();
instance.halt();
}
}
}
Expand All @@ -170,11 +163,11 @@ public boolean reusedExistingDatabase() {
* @param dist Path to MySQL distribution
* @param target Where to keep temp data
* @param socketfile Alternative socket location for mysql (may be null)
* @return Process started
* @return DatabaseInstance containing the process and the thread
* @throws IOException If fails to start
* @checkstyle ParameterNumberCheck (10 lines)
*/
private Process process(@NotNull final Config config,
private DatabaseInstance process(@NotNull final Config config,
final File dist, final File target, final File socketfile)
throws IOException {
final File temp = this.prepareFolders(target);
Expand Down Expand Up @@ -211,24 +204,14 @@ private Process process(@NotNull final Config config,
}
}
final Process proc = builder.start();
final Thread thread = new Thread(
new VerboseRunnable(
new Callable<Void>() {
@Override
public Void call() throws Exception {
new VerboseProcess(proc).stdoutQuietly();
return null;
}
}
)
);
final VerboseProcessThread thread = new VerboseProcessThread(proc);
thread.setDaemon(true);
thread.start();
this.waitFor(socket, config.port());
if (this.clean) {
this.configure(config, dist, socket);
}
return proc;
return new DatabaseInstance(proc, thread);
}

/**
Expand Down Expand Up @@ -441,4 +424,101 @@ private void setClean(final File target, final boolean deldir) {
Logger.info(this, "reuse existing database %s", !this.clean);
}


private class DatabaseInstance {

private final Process process;
private final VerboseProcessThread thread;
private Thread shutdownThread = null;

public DatabaseInstance(Process process, VerboseProcessThread thread) {
this.process = process;
this.thread = thread;
}

public Process getProcess() {
return process;
}

public VerboseProcessThread getThread() {
return thread;
}

public void addShutdownHook(final int port) {
if (shutdownThread == null) {
shutdownThread = new Thread(
new Runnable() {
@Override
public void run() {
Instances.this.stop(port);
}
}
);
Runtime.getRuntime().addShutdownHook(shutdownThread);
}
}

public void removeShutdownHook() {
if (shutdownThread != null) {
Runtime.getRuntime().removeShutdownHook(shutdownThread);
shutdownThread = null;
}
}

public void halt() {
thread.halt();
if (process != null) {
IOUtils.closeQuietly(process.getInputStream());
IOUtils.closeQuietly(process.getOutputStream());
IOUtils.closeQuietly(process.getErrorStream());
process.destroy();
}
}
}

private class VerboseProcessThread extends Thread {

private final VerboseRunnable runnable;
private transient boolean stop = false;

public VerboseProcessThread(final Process process) {
this.runnable = new VerboseRunnable( new Callable<Void>() {
@Override
public Void call() throws Exception {
VerboseProcess verboseProcess = new VerboseProcess(process);
try {
verboseProcess.stdoutQuietly();
} catch (RuntimeException e) {
if(!stop) {
throw e;
}
} finally {
verboseProcess.close();
}
return null;
}
});
}

@Override
public void run() {
try {
runnable.run();
} catch (RuntimeException e) {
if(!stop) {
throw e;
}
}
}

public void halt() {
stop = true;
this.interrupt();
try {
TimeUnit.SECONDS.timedJoin(this, 3L);
} catch (InterruptedException e) {
Logger.warn(this, "Can't stop the thread", e);
}
}
}
}

0 comments on commit e157fdc

Please sign in to comment.