Permalink
Browse files

added javascript and less/css request handler

  • Loading branch information...
1 parent 7ebc6cf commit 1bbff6a8e32fba8b50d1ce8898d271fc3ffabc4b @hofmeister committed Feb 2, 2013
@@ -2,7 +2,7 @@
import com.vonhof.babelshark.BabelShark;
import com.vonhof.babelshark.language.JsonLanguage;
-import com.vonhof.webi.FileRequestHandler;
+import com.vonhof.webi.file.FileRequestHandler;
import com.vonhof.webi.HttpMethod;
import com.vonhof.webi.Webi;
import com.vonhof.webi.annotation.Body;
View
@@ -66,5 +66,20 @@
<artifactId>jetty-servlet</artifactId>
<version>8.1.4.v20120524</version>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.javascript</groupId>
+ <artifactId>closure-compiler</artifactId>
+ <version>r2388</version>
+ </dependency>
+ <dependency>
+ <groupId>com.asual.lesscss</groupId>
+ <artifactId>lesscss-engine</artifactId>
+ <version>1.3.0</version>
+ </dependency>
</dependencies>
</project>
@@ -1,5 +1,9 @@
-package com.vonhof.webi;
+package com.vonhof.webi.file;
+import com.vonhof.webi.HttpException;
+import com.vonhof.webi.RequestHandler;
+import com.vonhof.webi.Webi;
+import com.vonhof.webi.WebiContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -35,6 +39,7 @@
*/
private String indexFileName = "index.html";
+ @Override
public final void handle(WebiContext ctxt) throws IOException, ServletException {
String filePath = String.format("%s%s",docRoot,ctxt.getPath());
File file = new File(filePath);
@@ -49,7 +54,7 @@ public final void handle(WebiContext ctxt) throws IOException, ServletException
File indexFile = new File(indexFilePath);
if (!indexFile.exists()) {
- serveDir(ctxt, file);
+ serveDir(ctxt, file);
return;
} else {
file = indexFile;
@@ -60,10 +65,14 @@ public final void handle(WebiContext ctxt) throws IOException, ServletException
if (file.exists()) {
serveFile(ctxt, file);;
} else {
- ctxt.sendError(new HttpException(HttpException.NOT_FOUND,"Not found"));
- if (!file.getName().equalsIgnoreCase("favicon.ico")) {
- System.out.println("File not found:"+filePath);
- }
+ unknownFile(ctxt, file, filePath);
+ }
+ }
+
+ protected void unknownFile(WebiContext ctxt,File file,String path) throws IOException {
+ ctxt.sendError(new HttpException(HttpException.NOT_FOUND,"Not found"));
+ if (!file.getName().equalsIgnoreCase("favicon.ico")) {
+ System.out.println("File not found:"+path);
}
}
@@ -105,8 +114,17 @@ protected void serveDir(WebiContext req,File dir) throws IOException {
*/
protected void serveFile(WebiContext req,File file) throws IOException {
req.setHeader("Content-type",getResponseType(file));
- if (!webi.isDevMode())
- req.setDateHeader("Last-Modified",file.lastModified());
+
+ long lastModified = file.lastModified();
+ long reqLastModified = req.getRequest().getDateHeader("If-Modified-Since");
+
+ req.setDateHeader("Last-Modified",lastModified);
+
+ if (reqLastModified > 0 && lastModified <= reqLastModified) {
+ req.setStatus(304);
+ req.flushBuffer();
+ return;
+ }
FileInputStream fileIn = new FileInputStream(file);
while(fileIn.available() > 0) {
@@ -121,13 +139,23 @@ protected void serveFile(WebiContext req,File file) throws IOException {
* @return
*/
protected String getResponseType(File file) {
- String[] parts = file.getName().split("\\.");
- String ext = parts[parts.length-1].toLowerCase();
+ String ext = getFileExt(file);
String mimeType = mimeTypes.get(ext);
- if (mimeType == null)
+ if (mimeType == null) {
mimeType = defaultMimeType;
+ }
return mimeType;
}
+
+ /**
+ * Get file extension
+ * @param file
+ * @return
+ */
+ protected String getFileExt(File file) {
+ String[] parts = file.getName().split("\\.");
+ return parts[parts.length-1].toLowerCase();
+ }
/**
* Set default index file for directories
@@ -175,22 +203,26 @@ public void setDefaultMimeType(String defaultMimeType) {
*/
public static FileRequestHandler getStandardFileHandler() {
FileRequestHandler out = new FileRequestHandler();
- out.addMimeType("jpg","image/jpeg");
- out.addMimeType("png","image/png");
- out.addMimeType("gif","image/gif");
- out.addMimeType("bmp","image/bmp");
- out.addMimeType("html","text/html");
- out.addMimeType("htm","text/html");
- out.addMimeType("css","text/css");
- out.addMimeType("json","application/json");
- out.addMimeType("xml","text/xml");
- out.addMimeType("js","text/javascript");
- out.addMimeType("woff","application/x-font-woff");
- out.addMimeType("ttf","font/ttf");
- out.addMimeType("eot","font/eot");
- out.addMimeType("otf","font/otf");
+ out.addDefaultMimeTypes();
return out;
}
+
+ public void addDefaultMimeTypes() {
+ addMimeType("jpg","image/jpeg");
+ addMimeType("png","image/png");
+ addMimeType("gif","image/gif");
+ addMimeType("bmp","image/bmp");
+ addMimeType("html","text/html");
+ addMimeType("htm","text/html");
+ addMimeType("css","text/css");
+ addMimeType("json","application/json");
+ addMimeType("xml","text/xml");
+ addMimeType("js","text/javascript");
+ addMimeType("woff","application/x-font-woff");
+ addMimeType("ttf","font/ttf");
+ addMimeType("eot","font/eot");
+ addMimeType("otf","font/otf");
+ }
}
@@ -0,0 +1,175 @@
+package com.vonhof.webi.file;
+
+import com.google.common.io.Files;
+import com.google.javascript.jscomp.CompilationLevel;
+import com.google.javascript.jscomp.Compiler;
+import com.google.javascript.jscomp.CompilerOptions;
+import com.google.javascript.jscomp.JSModule;
+import com.google.javascript.jscomp.Result;
+import com.google.javascript.jscomp.SourceFile;
+import com.google.javascript.jscomp.SourceMap;
+import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException;
+import com.vonhof.webi.WebiContext;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.io.IOUtils;
+
+
+/**
+ *
+ * @author Henrik Hofmeister <@vonhofdk>
+ */
+public class JavascriptHandler extends PreprocessingRequestHandler {
+
+ private final static Pattern modulePattern = Pattern.compile("(?uis)^//@module\\s+([A-Z\\.\\-_][A-Z0-9\\.\\-_]+)(?:\\s+@prio ([0-9])+)?");
+
+ private final CompilerOptions options = new CompilerOptions();
+
+ private final Charset charset = Charset.forName("UTF-8");
+
+ private final Map<String,String> sourceMaps = new HashMap<String, String>();
+
+ public JavascriptHandler() {
+ super("text/javascript");
+ }
+
+ @Override
+ protected boolean isValid(File file) {
+ String ext = getFileExt(file);
+ return ext.equalsIgnoreCase("js") || ext.equalsIgnoreCase("map");
+ }
+
+
+ @Override
+ protected void outputFiles(WebiContext req, List<File> files) throws IOException {
+ if (req.getParameterMap().contains("source")) {
+ super.outputFiles(req, files);
+ } else {
+ compiled(req, files);
+ }
+ }
+
+ private void compiled(WebiContext req, List<File> files) throws IOException {
+ final Compiler compiler = new Compiler();
+ final String sourceName = req.getRequest().getRequestURI();
+ boolean outputMap = req.getParameterMap().contains("map");
+ if (outputMap) {
+ req.setHeader("Content-type", "application/json");
+ if (sourceMaps.containsKey(sourceName)) {
+ IOUtils.write(sourceMaps.get(sourceName), req.getOutputStream());
+ return;
+ }
+ }
+
+ final CompilerOptions options = new CompilerOptions();
+ CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
+ options.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT5);
+ options.sourceMapDetailLevel = SourceMap.DetailLevel.ALL;
+ options.sourceMapOutputPath = sourceName+"?map";
+
+ //options.setNameReferenceReportPath("./");
+
+ final ArrayList<SourceFile> externs = new ArrayList<SourceFile>();
+ final Map<String,JSModule> modules = new HashMap<String, JSModule>();
+ JSModule rootModule = new JSModule("global");
+ modules.put("__global", rootModule);
+
+ rootModule.add(SourceFile.fromCode("root.js", ""));
+
+
+
+ //externs.add(SourceFile.fromCode("webbeans.js", "var $wb = {};var $cs = {};"));
+
+ final String baseDir = this.getDocumentRoot()+req.getPath();
+ final Map<String,String> dependencies = new HashMap<String, String>();
+
+ for(File file:files) {
+
+ String relativePath = req.getBase()+file.getAbsolutePath().substring(this.getDocumentRoot().length()+1);
+ String fileName = file.getAbsolutePath().substring(baseDir.length());
+ if (fileName.isEmpty()) {
+ fileName = file.getName();
+ }
+ fileName += "?source";
+
+ SourceFile sFile = SourceFile.fromCode(fileName,relativePath,Files.toString(file, charset));
+
+ String firstLine = Files.readFirstLine(file, charset);
+ Matcher m = modulePattern.matcher(firstLine);
+ String moduleName = null;
+ int order = 0;
+ if (m.find()) {
+ moduleName = m.group(1);
+ if (m.groupCount() > 1 && m.group(2) != null) {
+ order = Integer.parseInt(m.group(2));
+ }
+ }
+
+ if (moduleName == null || moduleName.isEmpty()) {
+ moduleName = "__global";
+ }
+ if (!modules.containsKey(moduleName)) {
+ modules.put(moduleName, new JSModule(moduleName));
+ }
+ if (order > 0) {
+ modules.get(moduleName).addFirst(sFile);
+ } else {
+ modules.get(moduleName).add(sFile);
+ }
+ }
+
+ for(Entry<String,JSModule> entry:modules.entrySet()) {
+ String moduleName = entry.getKey();
+
+ if (moduleName.indexOf(".") > -1) {
+ String[] parents = moduleName.split("\\.");
+ String parent = "";
+ for(int i = 0; i < parents.length-1;i++) {
+ parent += parents[i];
+ if (modules.get(parent) != null) {
+ entry.getValue().addDependency(modules.get(parent));
+ }
+ }
+ }
+ }
+ List<JSModule> moduleList = null;
+ try {
+ JSModule[] sortJsModules = JSModule.sortJsModules(modules.values());
+ moduleList = Arrays.asList(sortJsModules);
+ } catch (CircularDependencyException ex) {
+ throw new IOException(ex);
+ }
+
+ Compiler.setLoggingLevel(Level.SEVERE);
+
+ Result result = compiler.compileModules(externs,moduleList, options);
+
+ //Must generate sources before accessing source map
+ String source = compiler.toSource();
+
+ StringBuilder sb = new StringBuilder();
+ result.sourceMap.validate(true);
+ result.sourceMap.appendTo(sb, sourceName);
+ sourceMaps.put(sourceName, sb.toString());
+
+ if (outputMap) {
+ IOUtils.write(sourceMaps.get(sourceName), req.getOutputStream());
+ } else {
+ source += "\n//@ sourceMappingURL="+options.sourceMapOutputPath;
+ req.setHeader("X-SourceMap",options.sourceMapOutputPath);
+ IOUtils.write(source, req.getOutputStream());
+ }
+ }
+}
Oops, something went wrong. Retry.

0 comments on commit 1bbff6a

Please sign in to comment.