├── war └── WEB-INF │ ├── jack │ ├── narwhal │ ├── jackconfig.js │ ├── lib │ └── js.jar │ ├── appengine-web.xml │ └── web.xml ├── .gitignore ├── package.json ├── src └── org │ └── jackjs │ ├── JackFilter.java │ └── JackServlet.java └── README.md /war/WEB-INF/jack: -------------------------------------------------------------------------------- 1 | ../../../jack -------------------------------------------------------------------------------- /war/WEB-INF/narwhal: -------------------------------------------------------------------------------- 1 | ../../../narwhal -------------------------------------------------------------------------------- /war/WEB-INF/jackconfig.js: -------------------------------------------------------------------------------- 1 | ../../../jack/examples/example.js -------------------------------------------------------------------------------- /war/WEB-INF/lib/js.jar: -------------------------------------------------------------------------------- 1 | ../narwhal/platforms/rhino/jars/js.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | war/WEB-INF/lib/appengine-* 3 | war/WEB-INF/lib/datanucleus-* 4 | war/WEB-INF/lib/geronimo-* 5 | war/WEB-INF/lib/jdo2-* 6 | -------------------------------------------------------------------------------- /war/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jack-js 5 | 1 6 | 7 | 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jack-servelet", 3 | "dependencies": ["narwhal", "jack"], 4 | "author": "Tom Robinson (http://tlrobinson.net/)", 5 | "description": "A Java servlet for running JSGI applications", 6 | "keywords": ["jack", "java", "servlet", "jsgi"] 7 | } 8 | -------------------------------------------------------------------------------- /war/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jack 6 | org.jackjs.JackServlet 7 | 8 | 25 | 26 | 27 | 28 | 29 | jack 30 | /* 31 | 32 | 33 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/org/jackjs/JackFilter.java: -------------------------------------------------------------------------------- 1 | package org.jackjs; 2 | import java.io.File; 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletContext; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | @SuppressWarnings("serial") 16 | public class JackFilter extends JackServlet implements Filter { 17 | 18 | public void destroy() { 19 | } 20 | 21 | public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException { 22 | HttpServletRequest request = (HttpServletRequest) req; 23 | String realPath = request.getRealPath(request.getRequestURI()); 24 | File targetFile; 25 | boolean fileExists = realPath != null && (targetFile = new File(realPath)).exists() && targetFile.isFile(); 26 | if(fileExists){ 27 | chain.doFilter(req, response); 28 | } 29 | else{ 30 | service(request, (HttpServletResponse) response); 31 | } 32 | } 33 | 34 | public void init(final FilterConfig config) throws ServletException { 35 | initialize(new Config(){ 36 | public String getInitParameter(String name){ 37 | return config.getInitParameter(name); 38 | } 39 | public ServletContext getServletContext(){ 40 | return config.getServletContext(); 41 | } 42 | }); 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jack Servlet 2 | ============ 3 | 4 | Requirements 5 | ------------ 6 | 7 | 1. Narwhal: http://github.com/tlrobinson/narwhal 8 | 2. Jack: http://github.com/tlrobinson/jack 9 | 10 | AppEngine Instructions 11 | ---------------------- 12 | 13 | 1. Set the APPENGINE_JAVA_SDK environment variable to the location of the AppEngine SDK (http://code.google.com/appengine/downloads.html), or change the "sdk.dir" property in the build.xml file. 14 | 2. Ensure "war/WEB-INF/narwhal" is a Narwhal distribution (currently symlinked to "narwhal" in the parent directory) and "war/WEB-INF/narwhal/packages/jack" is a Jack distribution. 15 | 4. Place your Jack application in "war/WEB-INF" with the main module called "jackconfig.js", which exports the main Jack application as "app". 16 | 5. "ant runserver" to run locally. 17 | 6. Edit the AppEngine application ID in "war/WEB-INF/appengine-web.xml". 18 | 7. "ant update" to deploy. 19 | 20 | Other Servlet Container Instructions 21 | ------------------------------------ 22 | 23 | Coming soon. 24 | 25 | Notes 26 | ----- 27 | 28 | * You can change the default modules path, module name, application name, environment name using the "modulesPath", "module", "app", and "environment" init-params in web.xml, i.e.: 29 | 30 | 31 | jack 32 | org.jackjs.JackServlet 33 | 34 | modulesFilePath 35 | WEB-INF 36 | 37 | 38 | narwhalFilePath 39 | /path/to/narwhal 40 | 41 | 42 | module 43 | jackconfig.js 44 | 45 | 46 | app 47 | app 48 | 49 | 50 | environment 51 | production 52 | 53 | 54 | 55 | * In addition to the "war/WEB-INF/narwhal" symlink, there are relative symlinks to Rhino in "war/WEB-INF/lib/js.jar" and an example application at "war/WEB-INF/jackconfig.js" 56 | -------------------------------------------------------------------------------- /src/org/jackjs/JackServlet.java: -------------------------------------------------------------------------------- 1 | package org.jackjs; 2 | import java.io.File; 3 | import java.io.FileReader; 4 | import java.io.IOException; 5 | 6 | import javax.servlet.ServletConfig; 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.mozilla.javascript.Context; 14 | import org.mozilla.javascript.Function; 15 | import org.mozilla.javascript.ImporterTopLevel; 16 | import org.mozilla.javascript.Scriptable; 17 | import org.mozilla.javascript.ScriptableObject; 18 | import org.mozilla.javascript.WrappedException; 19 | 20 | @SuppressWarnings("serial") 21 | public class JackServlet extends HttpServlet { 22 | Scriptable scope; 23 | Scriptable servletHandler; 24 | Function handler; 25 | interface Config{ 26 | String getInitParameter(String name); 27 | ServletContext getServletContext(); 28 | } 29 | 30 | public void init(final ServletConfig config) throws ServletException { 31 | super.init(config); 32 | initialize(new Config(){ 33 | public String getInitParameter(String name){ 34 | return config.getInitParameter(name); 35 | } 36 | public ServletContext getServletContext(){ 37 | return JackServlet.this.getServletContext(); 38 | } 39 | }); 40 | } 41 | public void initialize(Config config) throws ServletException { 42 | String modulesPathDefault = System.getProperty("narwhal.modules.path"); 43 | if(modulesPathDefault== null){ 44 | modulesPathDefault = ""; 45 | } 46 | final String[] modulesPath = getInitParam(config, "modulesFilePath",modulesPathDefault).split(","); 47 | final String configPath = config.getServletContext().getRealPath(getInitParam(config, "configPath", "WEB-INF")); 48 | final String moduleName = getInitParam(config, "module", "jackconfig.js"); 49 | final boolean reload = "true".equals(getInitParam(config, "reload", "true")); 50 | final String appName = getInitParam(config, "app", "app"); 51 | final String environmentName = getInitParam(config, "environment", "development"); 52 | String narwhalHomeDefault = System.getProperty("narwhal.home"); 53 | if(narwhalHomeDefault == null){ 54 | narwhalHomeDefault = config.getServletContext().getRealPath("WEB-INF/narwhal"); 55 | } 56 | try { 57 | narwhalHomeDefault = new File(narwhalHomeDefault).getCanonicalPath(); 58 | } catch (IOException e) { 59 | // TODO Auto-generated catch block 60 | e.printStackTrace(); 61 | } 62 | final String narwhalHome = getInitParam(config, "narwhalFilePath", narwhalHomeDefault); 63 | final String narwhalFilename = "engines/rhino/bootstrap.js"; 64 | 65 | Context context = Context.enter(); 66 | try { 67 | context.setOptimizationLevel(5); 68 | scope = new ImporterTopLevel(context); 69 | 70 | ScriptableObject.putProperty(scope, "NARWHAL_HOME", Context.javaToJS(narwhalHome, scope)); 71 | 72 | // load Narwhal 73 | context.evaluateReader(scope, new FileReader(narwhalHome+"/"+narwhalFilename), narwhalFilename, 1, null); 74 | 75 | // load Servlet handler "process" method 76 | servletHandler = (Scriptable)context.evaluateString(scope, "new (require('jack/handler/servlet').Servlet)({reload:" + reload + ", app:'" + appName + "', environment:'"+ environmentName + "', args:['"+configPath.replaceAll("\\\\", "/") +"/"+moduleName+"']})", null, 1, null); 77 | 78 | handler = (Function) ScriptableObject.getProperty(servletHandler, "process"); 79 | 80 | for (String modulePath : modulesPath){ 81 | context.evaluateString(scope, "require.paths.push('"+modulePath.replaceAll("\\\\", "/")+"');", null, 1, null); 82 | } 83 | 84 | 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | } finally { 88 | Context.exit(); 89 | } 90 | } 91 | 92 | public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { 93 | Context context = Context.enter(); 94 | try { 95 | Object args[] = {request, response}; 96 | handler.call(context, scope, servletHandler, args); 97 | } 98 | catch(WrappedException e){ 99 | // this allows Jetty's RetryRequest exceptions to escape properly, and just 100 | // improves visibility of exceptions 101 | if(e.getWrappedException() instanceof RuntimeException) 102 | throw (RuntimeException) e.getWrappedException(); 103 | throw e; 104 | } 105 | finally { 106 | Context.exit(); 107 | } 108 | } 109 | 110 | private String getInitParam(Config config, String name, String defaultValue) { 111 | String value = config.getInitParameter(name); 112 | return value == null ? defaultValue : value; 113 | } 114 | } 115 | --------------------------------------------------------------------------------