package com.sampullara.mustache;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.github.mustachejava.TemplateFunction;
import com.google.common.base.Function;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.MappingJsonFactory;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import com.google.common.base.Throwables;
import com.google.common.io.Files;
import com.sampullara.cli.Args;
import com.sampullara.cli.Argument;
/**
* Run a local server and merge .js and .html files using mustache.
* <p/>
* User: sam Date: Jun 15, 2010 Time: 4:25:31 PM
*/
public class Handlebar {
@Argument(alias = "p")
private static Integer port = 8000;
@Argument(alias = "d", required = true)
private static String dir;
@Argument(alias = "m")
private static String mocks;
private static File rootDir;
private static final FileTypeMap FILE_TYPE_MAP;
private static final JsonFactory JSON_FACTORY = new MappingJsonFactory();
static {
FILE_TYPE_MAP = loadFileTypeMapFromContextSupportModule();
}
private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
// see if we can find the extended mime.types from the context-support module
InputStream is = ClassLoader.getSystemResourceAsStream("com/sampullara/mustache/mimes.txt");
if (null != is) {
return new MimetypesFileTypeMap(is);
}
return FileTypeMap.getDefaultFileTypeMap();
}
public static Object toObject(final JsonNode node) {
if (node.isArray()) {
return new ArrayList() {
{
for (JsonNode jsonNodes : node) {
add(toObject(jsonNodes));
}
}
};
} else if (node.isObject()) {
return new HashMap() {
{
for (Iterator<Map.Entry<String, JsonNode>> i = node.getFields(); i.hasNext();) {
Map.Entry<String, JsonNode> next = i.next();
Object o = toObject(next.getValue());
put(next.getKey(), o);
}
}
};
} else if (node.isBoolean()) {
return node.getBooleanValue();
} else if (node.isNull()) {
return null;
} else {
return node.asText();
}
}
public static void main(String[] args) throws Exception {
try {
Args.parse(Handlebar.class, args);
} catch (IllegalArgumentException e) {
Args.usage(Handlebar.class);
System.exit(1);
}
rootDir = new File(dir);
if (null == mocks) mocks = dir;
Handler handler = new AbstractHandler() {
public void handle(String s, Request r, HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
try {
String pathInfo = req.getPathInfo();
if (pathInfo.endsWith("/")) pathInfo += "index.html";
// obtain mime type
String mimeType = FILE_TYPE_MAP.getContentType(pathInfo);
System.out.println(String.format("%s: %s", mimeType, pathInfo));
// create a handle to the resource
File staticres = new File(rootDir, pathInfo.substring(1));
res.setContentType(mimeType == null ? "text/html" : mimeType);
res.setCharacterEncoding("utf-8");
if (mimeType == null || mimeType.equals("text/html")) {
// Handle like a template
String filename = pathInfo.substring(1);
// check if file exists
if (!staticres.exists()) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
processTemplate(req, res, "404.html");
} else {
res.setStatus(HttpServletResponse.SC_OK);
processTemplate(req, res, filename);
}
r.setHandled(true);
} else {
if (!staticres.exists()) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Handle like a file
res.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream out = res.getOutputStream();
Files.copy(staticres, out);
out.close();
r.setHandled(true);
}
} catch (Exception e) {
// params
Map<String, String> params = new HashMap<String, String>();
params.put("stacktrace", Throwables.getStackTraceAsString(e));
// render template
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
processTemplate(req, res, "500.html", params);
r.setHandled(true);
}
}
private void processTemplate(HttpServletRequest req, HttpServletResponse res,
String filename, Object... scopes) throws UnsupportedEncodingException,
FileNotFoundException, IOException, JsonParseException, JsonProcessingException {
if (!new File(rootDir, filename).exists()) {
System.out.println("template not found, skipping: " + filename);
return;
}
MustacheFactory mc = new DefaultMustacheFactory(rootDir);
Mustache mustache = mc.compile(filename);
String base = filename.substring(0, filename.lastIndexOf("."));
File file = new File(mocks, base + ".json");
Map parameters = new HashMap<Object, Object>(req.getParameterMap()) {
@Override
public Object get(Object o) {
Object result = super.get(o); // To change body of overridden methods use File |
// Settings | File Templates.
if (result instanceof String[]) {
String[] strings = (String[]) result;
if (strings.length == 1) {
return strings[0];
}
}
return result;
}
};
List<Object> scs = new ArrayList<Object>();
if (null != scopes) scs.addAll(Arrays.<Object>asList(scopes));
scs.add(parameters);
scs.add(new Object() {
Function slots = new TemplateFunction() {
@Override
public String apply(String input) {
return "{{>" + input.trim() + "}}";
}
};
});
if (file.exists()) {
BufferedReader br =
new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
JsonParser parser = JSON_FACTORY.createJsonParser(br);
JsonNode json = parser.readValueAsTree();
br.close();
scs.add(0, toObject(json));
}
mustache.execute(res.getWriter(), scs.toArray());
}
private String simpleEscape(String string) {
return string.replace("&", "&").replace("<", "<").replace(">", ">");
}
};
Server server = new Server(port);
server.setHandler(handler);
server.start();
}
}