package net.fortytwo.sesametools.replay;
import info.aduna.iteration.CloseableIteration;
import org.openrdf.IsolationLevel;
import org.openrdf.model.ValueFactory;
import org.openrdf.sail.Sail;
import org.openrdf.sail.SailConnection;
import org.openrdf.sail.SailException;
import org.openrdf.sail.StackableSail;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A Sail which reads a log of previously recorded Sail operations and executes them.
*
* @author Joshua Shinavier (http://fortytwo.net).
*/
public class PlaybackSail implements StackableSail {
private final Sail baseSail;
private final Source<SailConnectionCall, SailException> querySource;
private final Map<String, SailConnection> idToConnectionMap;
private final Map<String, List<CloseableIteration>> idToIterationsMap;
public PlaybackSail(final Sail baseSail, Source<SailConnectionCall, SailException> querySource) {
this.baseSail = baseSail;
this.querySource = querySource;
this.idToConnectionMap = new HashMap<>();
this.idToIterationsMap = new HashMap<>();
}
public PlaybackSail(final Sail baseSail, final InputStream is) {
this.baseSail = baseSail;
this.querySource = new InputStreamSource(is);
this.idToConnectionMap = new HashMap<>();
this.idToIterationsMap = new HashMap<>();
}
public void setDataDir(final File file) {
baseSail.setDataDir(file);
}
public File getDataDir() {
return baseSail.getDataDir();
}
public void initialize() throws SailException {
// baseSail.initialize();
Handler<SailConnectionCall, SailException> handler = new Handler<SailConnectionCall, SailException>() {
private long line = 0;
public void handle(final SailConnectionCall call) throws SailException {
line++;
try {
String id = call.getId();
SailConnectionCall.Type type = call.getType();
if (id.contains("-")) {
CloseableIteration iter = getIteration(id);
call.execute(iter);
} else {
SailConnection sc = getConnection(id);
Object result = call.execute(sc);
if (SailConnectionCall.Type.CLOSE_CONNECTION == type) {
idToConnectionMap.remove(id);
idToIterationsMap.remove(id);
} else if (createsIteration(call)) {
addIteration(id, (CloseableIteration) result);
}
}
} catch (Throwable e) {
System.err.println("Error on line " + line);
throw (e instanceof SailException)
? (SailException) e
: new SailException(e);
}
}
};
querySource.writeTo(handler);
}
private SailConnection getConnection(final String id) throws SailException {
SailConnection sc = idToConnectionMap.get(id);
if (null == sc) {
sc = getConnection();
idToConnectionMap.put(id, sc);
}
return sc;
}
private CloseableIteration getIteration(final String id) {
int i = id.indexOf("-");
String conId = id.substring(0, i);
int iterIndex = new Integer(id.substring(i + 1)) - 1;
return idToIterationsMap.get(conId).get(iterIndex);
}
private boolean createsIteration(final SailConnectionCall call) {
switch (call.getType()) {
case GET_CONTEXT_IDS:
case GET_NAMESPACES:
case GET_STATEMENTS:
return true;
default:
return false;
}
}
private void addIteration(final String id, final CloseableIteration iter) {
List<CloseableIteration> iters = idToIterationsMap.get(id);
if (null == iters) {
iters = new ArrayList<>();
idToIterationsMap.put(id, iters);
}
iters.add(iter);
}
public void shutDown() throws SailException {
// Close any remaining open SailConnections
for (SailConnection sc : idToConnectionMap.values()) {
sc.close();
}
// baseSail.shutDown();
}
public boolean isWritable() throws SailException {
return baseSail.isWritable();
}
public SailConnection getConnection() throws SailException {
return baseSail.getConnection();
}
public ValueFactory getValueFactory() {
return baseSail.getValueFactory();
}
@Override
public List<IsolationLevel> getSupportedIsolationLevels() {
return baseSail.getSupportedIsolationLevels();
}
@Override
public IsolationLevel getDefaultIsolationLevel() {
return baseSail.getDefaultIsolationLevel();
}
public void setBaseSail(final Sail sail) {
// Do nothing -- base Sail is final
}
public Sail getBaseSail() {
return baseSail;
}
private class InputStreamSource implements Source<SailConnectionCall, SailException> {
private BufferedReader reader;
public InputStreamSource(final InputStream is) {
reader = new BufferedReader(new InputStreamReader(is));
}
public void writeTo(final Handler<SailConnectionCall, SailException> handler) throws SailException {
try {
try {
String line;
while (null != (line = reader.readLine())) {
line = line.trim();
if (0 != line.length()) {
handler.handle(SailConnectionCall.construct(line));
}
}
} finally {
reader.close();
}
} catch (IOException e) {
throw new SailException(e);
}
}
}
}