package edu.stanford.nlp.sempre.cache;
import fig.basic.LogInfo;
import fig.basic.Option;
import fig.exec.Execution;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
/**
* Runs a server backed by a file which accepts requests of the following form:
* get\t|key| put\t|key|\t|value|
* <p/>
* You can test it out by doing: telnet <path> <port>
* <p/>
* To use it in code, see RemoteStringCache.
*
* @author Percy Liang
*/
public class StringCacheServer implements Runnable {
@Option(gloss = "Open port here", required = true) public int port;
@Option(gloss = "How much output to print") public int verbose = 0;
@Option(gloss = "Read only") public boolean readOnly = false;
@Option(gloss = "Only allow files in this directory") public String basePath;
// Shared state
private HashMap<String, FileStringCache> caches = new HashMap<String, FileStringCache>();
private boolean terminated = false;
// Represents the null value to be returned back to the user.
public static String nullString = "__NULL__";
class ClientHandler implements Runnable {
Socket client;
FileStringCache cache;
public ClientHandler(Socket client) {
this.client = client;
}
public void run() {
try {
PrintWriter out = new PrintWriter(client.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String line;
int numGets = 0, numPuts = 0, numErrors = 0;
while (!terminated && (line = in.readLine()) != null) {
// LogInfo.logs("Input: %s", line);
String[] tokens = line.split("\t");
String response = null;
if (tokens[0].equals("open") && tokens.length == 2) {
if (basePath != null && tokens[1].contains("/")) {
response = "ERROR: only simple file names allowed";
} else {
String path = tokens[1];
if (basePath != null)
path = new File(basePath, path).toString();
// Create the cache if necessary
synchronized (caches) {
cache = caches.get(path);
if (cache == null) {
cache = new FileStringCache();
caches.put(path, cache);
}
}
response = "OK";
synchronized (cache) {
if (cache.getPath() == null) {
LogInfo.begin_track("Loading %s", path);
try {
cache.init(path, readOnly);
} catch (Throwable t) {
response = "ERROR: " + t;
}
LogInfo.logs("Response: %s", response);
LogInfo.end_track();
}
}
}
} else if (tokens[0].equals("get") && tokens.length == 2) {
if (cache == null) {
response = "ERROR: no file opened yet";
} else {
synchronized (cache) { response = cache.get(tokens[1]); }
if (response == null) response = nullString;
numGets++;
}
} else if (tokens[0].equals("put") && tokens.length == 3) {
if (readOnly) {
response = "ERROR: read-only";
} else if (cache == null) {
response = "ERROR: no file opened yet";
} else {
synchronized (cache) { cache.put(tokens[1], tokens[2]); }
response = "OK";
numPuts++;
}
} else if (tokens[0].equals("stats")) {
response = "Caches:";
synchronized (caches) {
for (String path : caches.keySet())
response += "\n " + path + " (" + caches.get(path).size() + " entries)";
}
} else if (tokens[0].equals("terminate")) {
if (readOnly) {
response = "ERROR: read-only";
} else {
response = "OK; telnet to the port again to terminate";
terminated = true;
}
} else if (tokens[0].equals("help")) {
response = "Commands (tab-separated):\n open |path|\n get |key|\n put |key| |value|\n terminate\n stats\n help";
} else {
response = "ERROR: " + line;
numErrors++;
}
// LogInfo.logs("Response: %s", response);
out.println(response);
out.flush();
}
in.close();
LogInfo.logs("[%s] Closed connection %s: %d gets, %d puts, %d errors", new Date(), client, numGets, numPuts, numErrors);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void run() {
LogInfo.logs("[%s] Starting server on port %d", new Date(), port);
try {
ServerSocket server = new ServerSocket(port);
while (!terminated) {
Socket client = server.accept();
LogInfo.logs("[%s] Opened connection from %s", new Date(), client);
Thread t = new Thread(new ClientHandler(client));
t.start();
}
LogInfo.log("Done");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
Execution.run(args,
new StringCacheServer(),
"FileStringCache", FileStringCache.opts);
}
}