package restx;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import restx.http.HTTP;
import restx.http.HttpStatus;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
/**
* Date: 28/11/13
* Time: 21:46
*/
public class FSRouter {
public static FSRouter mount(String fsPath) {
return new FSRouter(fsPath);
}
private String fsPath;
private boolean readonly;
private boolean allowDirectoryListing;
private FSRouter(String fsPath) {
this.fsPath = fsPath;
}
public RestxRouter on(String onPath) {
if (!onPath.endsWith("/")) {
onPath = onPath + "/";
}
final Path root = Paths.get(fsPath);
RestxRouter.Builder builder = RestxRouter.builder().name("FS:" + fsPath)
.addRoute(new StdRoute("FS:GET:" + fsPath, new StdRestxRequestMatcher("GET", onPath + "{path:.*}")) {
@Override
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx)
throws IOException {
String path = match.getPathParam("path");
File file = root.resolve(path).toFile();
if (!file.exists()) {
notFound(match, resp);
return;
}
if (file.isFile()) {
resp.setStatus(HttpStatus.OK);
resp.setContentType(HTTP.getContentTypeFromExtension(file.getName()).or("application/binary"));
Files.asByteSource(file).copyTo(resp.getOutputStream());
} else if (file.isDirectory() && allowDirectoryListing) {
resp.setStatus(HttpStatus.OK);
resp.setContentType("application/json");
PrintWriter writer = resp.getWriter();
writer.println("[");
Path dir = file.toPath();
for (Iterator<Path> iterator = java.nio.file.Files.newDirectoryStream(dir).iterator(); iterator.hasNext(); ) {
Path s = iterator.next();
boolean isDirectory = s.toFile().isDirectory();
writer.println("\"" + dir.relativize(s) + (isDirectory ? "/" : "") + "\""
+ (iterator.hasNext() ? "," : "") +"");
}
writer.println("]");
} else {
throw new WebException(HttpStatus.UNAUTHORIZED);
}
}
@Override
public String toString() {
return getMatcher().toString() + " => FS:" + root.toFile().getAbsolutePath();
}
});
if (!readonly) {
builder.addRoute(new StdRoute("FS:PUT:" + fsPath, new StdRestxRequestMatcher("PUT", onPath + "{path:.*}")) {
@Override
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx)
throws IOException {
String path = match.getPathParam("path");
File file = root.resolve(path).toFile();
if (file.exists() && !file.isFile()) {
throw new WebException(HttpStatus.UNAUTHORIZED);
}
if (path.endsWith("/")) {
if (!file.mkdirs()) {
throw new WebException(HttpStatus.UNAUTHORIZED);
} else {
resp.setStatus(HttpStatus.CREATED);
return;
}
}
if (!file.exists()) {
// ensure parent exist
if (!file.getParentFile().exists()) {
if (!file.getParentFile().mkdirs()) {
throw new WebException(HttpStatus.UNAUTHORIZED);
}
}
Files.asByteSink(file).writeFrom(req.getContentStream());
resp.setStatus(HttpStatus.CREATED);
} else {
Files.asByteSink(file).writeFrom(req.getContentStream());
resp.setStatus(HttpStatus.ACCEPTED);
}
}
@Override
public String toString() {
return getMatcher().toString() + " => FS:" + root.toFile().getAbsolutePath();
}
});
builder.addRoute(new StdRoute("FS:DELETE:" + fsPath, new StdRestxRequestMatcher("DELETE", onPath + "{path:.*}")) {
@Override
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx)
throws IOException {
String path = match.getPathParam("path");
File file = root.resolve(path).toFile();
if (!file.exists()) {
// nothing to do, send NO_CONTENT to be idempotent
resp.setStatus(HttpStatus.NO_CONTENT);
return;
}
if (file.isDirectory()) {
if (file.list().length > 0) {
throw new WebException(HttpStatus.UNAUTHORIZED, "can't delete non empty directory");
}
}
java.nio.file.Files.delete(file.toPath());
resp.setStatus(HttpStatus.NO_CONTENT);
}
@Override
public String toString() {
return getMatcher().toString() + " => FS:" + root.toFile().getAbsolutePath();
}
});
}
return builder
.build();
}
public FSRouter readonly() {
readonly = true;
return this;
}
public FSRouter allowDirectoryListing() {
allowDirectoryListing = true;
return this;
}
}