package com.mastfrog.acteur;
import com.mastfrog.acteur.spi.ApplicationControl;
import com.mastfrog.util.Checks;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A registry of resources (for example, streams or JDBC objects) which should
* be closed if the connection terminates while a response is being processed.
* It is common to, say, open a result set, and then drizzle it out one row at a
* time; if the client closes the connection, registering the object with the
* Closables instance tied to this request guarantees it is closed if the
* channel is.
*
* @author Tim Boudreau
*/
public final class Closables {
private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
private final List<Timer> timers = new CopyOnWriteArrayList<>();
private final CloseWhenChannelCloses closeListener = new CloseWhenChannelCloses();
private final ApplicationControl application;
Closables(Channel channel, ApplicationControl application) {
channel.closeFuture().addListener(closeListener);
this.application = application;
}
public final <T extends AutoCloseable> T add(T closable) {
Checks.notNull("closeable", closable);
if (!closeables.contains(closable)) {
closeables.add(closable);
}
return closable;
}
public final <T extends Timer> T add(T timer) {
Checks.notNull("timer", timer);
if (!timers.contains(timer)) {
timers.add(timer);
}
return timer;
}
public final Closables add(Runnable run) {
Checks.notNull("run", run);
for (AutoCloseable clos : closeables) {
if (clos instanceof RunnableWrapper) {
RunnableWrapper w = (RunnableWrapper) clos;
if (w.run == w.run) {
return this;
}
}
}
add(new RunnableWrapper(run));
return this;
}
class CloseWhenChannelCloses implements ChannelFutureListener {
@Override
public void operationComplete(ChannelFuture f) throws Exception {
close();
}
}
static class RunnableWrapper implements AutoCloseable {
private final Runnable run;
public RunnableWrapper(Runnable run) {
this.run = run;
}
@Override
public void close() throws Exception {
run.run();
}
}
void close() throws Exception {
for (AutoCloseable ac : closeables) {
try {
ac.close();
} catch (Exception e1) {
application.internalOnError(e1);
}
}
for (Timer t : timers) {
try {
t.cancel();
} catch (Exception e2) {
application.internalOnError(e2);
}
}
}
}