├── 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 |
--------------------------------------------------------------------------------