Table Of Content

  1. The JudoScript HTTP Server
  2. Serve Static Content
    » Secure Content with Realms
  3. Handle HTTP Requests with Plain Functions
    » Map URIs to HTTP Handler Functions
    » Case Study: File Upload
  4. Serve Servlets
  5. Serve JuSP Pages
  6. Servlet API Conformity and JuSP Compatibility
  7. Summary
  8. Code Listing

JudoScript HTTP Server with Servlet and JuSP Support

By James Jianbo Huang    December 2005       non-printer version

Abstract   JudoScript has an embedded HTTP server that not only delivers static content, but also supports web applications with three popular ways: Java servlet API, JuSP page engine and JudoScript functions for HTTP handling. Simply create the HTTP server object with the system function createHttpServer(), programmatically customize its behavior such as adding servlets, handler functions and/or directory mapping and setting runtime parameters, then call its start() method. This server can be used to quickly develop and test Java servlets and JuSP pages, and is also perfect for Java web applications with reasonable loads. You can use any object-mapping such as Hibernate and JDO or plain JDBC accesses.


 

1. The JudoScript HTTP Server

The following code creates a HTTP server in JudoScript:

Listing 1. simplest_http_server.judo
1: createHttpServer(8080, 'docroot/').start();

When run, it prints out these messages:

*** JudoScript HTTP Server is ready on port 8080 (base: "docroot/") ***

To stop the server gracefully, enter "q" <ENTER>:
Quite obviously, this line creates a HTTP server on port 8080 with docroot/ as its root directory to serve files; you can certainly specify an absolute path to a location. The parameters to createHttpServer() can be omitted; if so, the default root directory would be the current directory, and the default port number is 8080.

This server does a lot more than just serving files; it supports the following features:

  1. Serves static content
  2. Handles HTTP requests with plain JudoScript functions
  3. Serves JuSP pages
  4. Supports Java Servlets
We will detail on each use. Each feature will be presented with a simple example that just displays a message, and a "snoop" example that prints out all the relevant parameters and attributes. Before we start, let's list the public methods that the HTTP server object allows:
setPort(port)
setSessionTimeout(minutes)
setLogUserAccess(doLog, logUserAgent, logReferer)
setBrowseDirectory(allow)
setSocketFactory(socketFactoryClass)
setQuitCommand(cmd)
addPathMapping(uri, realpath)
addFileThrottle(urlPat, bps)
addRealm(name, dir, user, password)
addServlet(urlPat, className, initParams)
addCallMapping(urlPat, functionRef)

 

»»» Top «««

 

2. Serve Static Content

When a server is started, the static content from the base directory are available to the browsers on the intranet or internet. JudoScript supplies MIME types for many file extensions. You can add specific MIME types via the system function, addMimeType(ext, mimetype).

You can map certain URI prefixes to locations other than within the base directory. The following example maps /src/ and /doc/ elsewhere.

Listing 2. serve_static.judo
1: var judoBase = 'C:/CVSROOT/judoscript-0.9';
2: 
3: var svr = createHttpServer(8080);
4: 
5: svr.addPathMapping('/doc', '${judoBase}/_build/docs');
6: svr.addPathMapping('/src', '${judoBase}/src/judo');
7: svr.addRealm('Source Code', '/src', 'james', 'pass');
8:
9: svr.start();

You can limit the delivering rates of certain files via the server object's addFileThrottle(urlPat, bps) method.

Secure Content with Realms

In the above listing, line 7 sets a realm for /src. This means that content in that directory are password protected.

 

»»» Top «««

 

3. Handle HTTP Requests with Plain Functions

The quickiest way to provide dynamic content is to use the built-in /judocall/ support; the name following that URI pattern is used as a JudoScript function name, as demonstrated below:

Listing 3. judocall.judo
 1: var svr = createHttpServer(); // on port 8080
 2: svr.addCallMapping('/rand', &randomNumbers);
 3: svr.start();
 4: 
 5: function randomNumbers request, response, servlet {
 6:   response.setContentType('text/html');
 7:
 8:   var pw = response.getWriter();
 9:   println<pw> '<html><body><h2>Some Random Numbers</h2><pre>';
10:   for i from 1 to 10 {
11;     println<pw> random(100.0) : 3.3;
12:   }
13:   println<pw> '</pre></body></html>';
14: }

Once the script is run, i.e., the server is up, go to the browser, type in this URL and see the result:

The function, randomNumbers(), will be invoked with three parameters:

  • a javax.servlet.http.HttpServletRequest object,
  • a javax.servlet.http.HttpServletResponse object, and
  • a javax.servlet.http.HttpServlet object.
In JudoScript, if the servlet object is not used, you don't have to specify it in the function declaration. In our simple example above, we obtained the writer from the response object, and print out some text. you will see a more elaborate example next.

Map URIs to HTTP Handler Functions

In the above example, the /judocall/ is like the old /cgi-bin/, which may or may cause discomfort in some beholders' eyes. You have the choice to hide it, though, by aliasing URIs via the server object's addCallMapping() method, as we did on line 2 in the above code listing. Type in this URL in the browser and you shall see the same result:

Let's create a snooper function to reveal more information. We intend to test various URLs with both /judocall/ and URI aliasing. Let's see the source code first.

Listing 4. snooper.judo
 1: var svr = createHttpServer(); // on port 8080
 2: svr.addCallMapping('/snooper*', &snooper);
 3: svr.start();
 4:
 5: function snooper request, response, servlet {
 6:   response.setContentType('text/plain');
 7:
 8:   var pw = response.getWriter();
 9:   println<pw> 'function snooper() output:', nl;
10:
11:   var context = servlet.getServletContext();
12:   println<pw> 'Context init parameters:';
13:   for key in context.getInitParameterNames() {
14:     println<pw> '   ${key} = ', context.getInitParameter(key);
15:   }
16: 
17:   println<pw> nl, 'Context attributes:';
18:   for key in context.getAttributeNames() {
19:     println<pw> '   ${key} = ', context.getAttribute(key);
20:   }
21: 
22:   println<pw> nl, 'Server and Request attributes:';
23:   for key in request.getAttributeNames() {
24:     println<pw> '   ${key} = ', request.getAttribute(key);
25:   }
26: 
27:   println<pw> nl,
28:     '      Servlet Name: ', servlet.getServletName(), nl,
29:     '          Protocol: ', request.getProtocol(), nl,
20:     '            Scheme: ', request.getScheme(), nl,
31:     '       Server Name: ', request.getServerName(), nl,
32:     '       Server Port: ', request.getServerPort(), nl,
33:     '       Server Info: ', context.getServerInfo(), nl,
34:     '       Remote Addr: ', request.getRemoteAddr(), nl,
35:     '       Remote Host: ', request.getRemoteHost(), nl,
36:     'Character Encoding: ', request.getCharacterEncoding(), nl,
37:     '    Content Length: ', request.getContentLength(), nl,
38:     '      Content Type: ', request.getContentType(), nl,
39:     '            Locale: ', request.getLocale(), nl,
40:     ' Request Is Secure: ', request.isSecure(), nl,
41:     '         Auth Type: ', request.getAuthType(), nl,
42:     '       HTTP Method: ', request.getMethod(), nl,
43:     '       Remote User: ', request.getRemoteUser(), nl,
44:     '       Request URI: ', request.getRequestURI(), nl,
45:     '      Context Path: ', request.getContextPath(), nl,
46:     '      Servlet Path: ', request.getServletPath(), nl,
47:     '         Path Info: ', request.getPathInfo(), nl,
48:     '        Path Trans: ', request.getPathTranslated(), nl,
49:     '      Query String: ', request.getQueryString(), nl;
50:
51:   println<pw> 'Parameter names in this request:';
52:   for key in request.getParameterNames() {
53:     println<pw> '   ${key} = ', request.getParameterValues(key);
54:   }
55:
56:   println<pw> nl, 'Headers in this request:';
57:   for key in request.getHeaderNames() {
58:     println<pw> '   ${key}: ', request.getHeader(key);
59:   }
60:
61:   println<pw> nl, 'Cookies in this request:';
62:   for cookie in request.getCookies() {
63:     println<pw> '   ', cookie.getName(), ' = ', cookie.getValue();
64:   }
65:
66:   var session = request.getSession();
67:   if session != null {
68:     println<pw> nl,
69:       'Requested Session Id: ', request.getRequestedSessionId(), nl,
70:       '  Current Session Id: ', session.getId(), nl,
71:       '        Created Time: ', session.getCreationTime(), nl,
72:       '  Last Accessed Time: ', session.getLastAccessedTime(), nl,
73:       '             Timeout: ', session.getMaxInactiveInterval();
74: 
75:     println<pw> 'Session attributes:';
76:     for name in session.getAttributeNames() {
77:       println<pw> '   ${name} = ', session.getAttribute(name);
78:     }
79:   }
80:
81:   flush<pw>;
82:   pw.close();
83:
84: } // end of snooper().

The following screen-shots shows the result of invoking the "snooper" URL.

Case Study: File Upload

Let's take a look at uploading content to a JudoScript HTTP server. This is a good use of function-based HTTP handling. We use the Jakarta commons FileUpload package to support the uploading operations. If not yet, please download the package and keep commons-fileupload-1.0.jar (or later) in the classpath before running the script.

Listing 5. upload.judo
 1: var uploadMaxMemorySize  = 5*1024*1024;
 2: var uploadMaxRequestSize = 5*1024*1024;
 3: var uploadTempDirectory  = System::getProperty("java.io.tmpdir");
 4: 
 5: println 'uploadMaxMemorySize  = ', uploadMaxMemorySize;
 6: println 'uploadMaxRequestSize = ', uploadMaxRequestSize;
 7: println 'uploadTempDirectory  = ', uploadTempDirectory;
 8: 
 9: 
10: function getUploads request, response {
11:   import org.apache.commons.fileupload.*;
12: 
13:   if (FileUpload::isMultipartContent(request)) {
14:     var upload = new java::DiskFileUpload();
15:     upload.setSizeThreshold( uploadMaxMemorySize );
16:     upload.setSizeMax( uploadMaxRequestSize );
17:     upload.setRepositoryPath( uploadTempDirectory );
18: 
19:     var cnt = 0;
20:     for fileItem in upload.parseRequest(request) {
21:       if (fileItem.isFormField()) {
22:         var fieldName = fileItem.fieldName;
23:         var fieldVal  = fileItem.getString();
24:         println 'Form field: ', fieldName, ' = ', fieldVal;
25:       } elif (fileItem.name.isNotEmpty()) { // is an upload
26:         var fileName = fileItem.name.getFileName();
27:         println 'Upload item --', nl,
28:                 '   field name: ', fileItem.fieldName, nl,
29:                 ' content type: ', fileItem.contentType, nl,
30:                 '    file name: ', fileName, nl,
31:                 '    file size: ', fileItem.size;
32: 
33:         var fname = uploadTempDirectory + '/' + fileName;
34:         { fileItem.write( new java::File(fname) );
35:           ++cnt;
36:         catch: println 'Uploading ${fileName} failed: ', $_;
37:         }
38:       }
39:     }
40:   }
41: 
42:   request.setAttribute('msg', 'Successfully uploaded ${cnt} files.');
43:   showUploadForm request, response;
44: }
45: 
46: function showUploadForm request, response {
47:   response.setContentType('text/html');
48:   var pw = response.getWriter();
49: 
50:   var msg = request.getAttribute('msg');
51:   if (msg != null)
52:     msg = '<b>${msg}</b><hr>';
53:   else
54:     msg = '<h2>Upload Files</h2>';
55:   println<pw> [[*
56:     <html>
57:     <body>(* msg *)<br>
58:     <form method="POST" enctype="multipart/form-data"
59:           action="/judocall/getUploads">
60:     File name: <input type=file name="filename1" ><br>
61:     File name: <input type=file name="filename2" ><br>
62:     Some text: <input type=text name="foo" value="" ><br>
63:     <input type=submit value="Upload" >
64:     </form>
65:     </body></html>
66:   *]];
67:   pw.close();
68: }
69: 
70: // Now, start the server with call alias:
71: var svr = createHttpServer();
72: svr.addCallMapping('/upload*', &showUploadForm);
73: svr.start();

Run the script and use the browser to pick files to upload:

When the "submit" button is clicked, in addition to saving the two files, the server console prints out these lines:

Upload item --
   field name: filename1
 content type: application/octet-stream
    file name: TestBrowser.exe
    file size: 24576
Upload item --
   field name: filename2
 content type: text/html
    file name: TongueTwister.htm
    file size: 23235
Form field: foo = aaaaa

 

»»» Top «««

 

4. Serve Servlets

The JudoScript HTTP server supports Java Servets. The code base claims to support most of the latest Java Servlet API, version 2.4. Like running any Java programs, to use servlets in this server, first compile them, put all the compiled classes and dependent libraries in the classpath before starting the JudoScript script. In the script, after you have created the server object, call the addServlet() method to add servlets. The method allows you to specify URI pattern to be mapped to the servlet and initial parameters, demonstrated below:

Listing 6. snoopy.judo
1: var simpleServlet = 'com.judoscript.user.httpserver.SimpleServlet';
2: var snoopServlet = 'com.judoscript.user.httpserver.SnoopServlet';
3:
4: var svr = createHttpServer();
5: svr.addServlet('/simple*', simpleServlet);
6: var initParams = { alfa:1, beta:'xyz' };
7: svr.addServlet('/snoopy*', snoopServlet, initParams);
8:
9: svr.start();

The following is a screen shot for running the "snoopy" servlet.

The SimpleServlet just prints out a simple text message. Its source code is listed below.

Listing 7. SimpleServlet.java
 1: public class SimpleServlet extends HttpServlet
 2: {
 3:   public void service(HttpServletRequest req, HttpServletResponse res)
 4:               throws ServletException, IOException
 5:   {
 6:     res.setContentType("text/html");
 7: 
 8:     PrintWriter out = res.getWriter();
 9:     out.println("<html><body><h1>A Simplest Servlet</h1></body></html>");
10:     out.close();
11:   }
12: }

The SnoopServlet prints out everything about the request, servlet and server.

Listing 8. SnoopServlet.java
  1: import java.io.*;
  2: import java.util.*;
  3: import javax.servlet.*;
  4: import javax.servlet.http.*;
  5:
  6: public class SnoopServlet extends HttpServlet
  7: {
  8:   public void service(HttpServletRequest req, HttpServletResponse res)
  9:               throws ServletException, IOException
  0:   {
 10:     Enumeration e;
 11:     PrintWriter out = res.getWriter();
 12:     res.setContentType("text/plain");
 13:
 14:     out.println("Snoop Servlet");
 15:     out.println();
 16:
 17:     e = getInitParameterNames();
 18:     if (e != null) {
 19:       out.println("Servlet init parameters:");
 20:       while (e.hasMoreElements()) {
 21:         String key = (String)e.nextElement();
 22:         String value = getInitParameter(key);
 23:         out.println("   " + key + " = " + value);
 24:       }
 25:       out.println();
 26:     }
 27:
 28:     ServletContext context = getServletContext();
 29:     e = context.getInitParameterNames();
 30:     if (e != null) {
 31:       out.println("Context init parameters:");
 32:       while (e.hasMoreElements()) {
 33:         String key = (String)e.nextElement();
 34:         Object value = context.getInitParameter(key);
 35:         out.println("   " + key + " = " + value);
 36:       }
 37:       out.println();
 38:     }
 39:
 40:     e = context.getAttributeNames();
 41:     if (e != null) {
 42:       out.println("Context attributes:");
 43:       while (e.hasMoreElements()) {
 44:         String key = (String)e.nextElement();
 45:         Object value = context.getAttribute(key);
 46:         out.println("   " + key + " = " + value);
 47:       }
 48:       out.println();
 49:     }
 50: 	
 51:     e = req.getAttributeNames();
 52:     if (e != null) {
 53:       out.println("Request attributes:");
 54:       while (e.hasMoreElements()) {
 55:         String key = (String)e.nextElement();
 56:         Object value = req.getAttribute(key);
 57:         out.println("   " + key + " = " + value);
 58:       }
 59:       out.println();
 60:     }
 61:
 62:     out.println("Servlet Name: " + getServletName());
 63:     out.println("Protocol: " + req.getProtocol());
 64:     out.println("Scheme: " + req.getScheme());
 65:     out.println("Server Name: " + req.getServerName());
 66:     out.println("Server Port: " + req.getServerPort());
 67:     out.println("Server Info: " + context.getServerInfo());
 68:     out.println("Remote Addr: " + req.getRemoteAddr());
 69:     out.println("Remote Host: " + req.getRemoteHost());
 70:     out.println("Character Encoding: " + req.getCharacterEncoding());
 71:     out.println("Content Length: " + req.getContentLength());
 72:     out.println("Content Type: "+ req.getContentType());
 73:     out.println("Locale: "+ req.getLocale());
 74:     out.println("Default Response Buffer: "+ res.getBufferSize());
 75:     out.println();
 76:
 77:     e = req.getParameterNames();
 78:     if (e != null) {
 79:       out.println("Parameter names in this req:");
 80:       while (e.hasMoreElements()) {
 81:         String key = (String)e.nextElement();
 82:         String[] values = req.getParameterValues(key);
 83:         out.print("   " + key + " = ");
 84:         for(int i = 0; i < values.length; i++)
 85:           out.print(values[i] + " ");
 86:         out.println();
 87:       }
 88:       out.println();
 89:     }
 90:
 91:     e = req.getHeaderNames();
 92:     if (e != null) {
 93:       out.println("Headers in this req:");
 94:       while (e.hasMoreElements()) {
 95:         String key = (String)e.nextElement();
 96:         String value = req.getHeader(key);
 97:         out.println("   " + key + ": " + value);
 98:       }
 99:       out.println();
100:     }
101:
102:     Cookie[] cookies = req.getCookies();
103:     if (cookies.length > 0) {
104:       out.println("Cookies in this req:");
105:       for (int i = 0; i < cookies.length; i++) {
106:         Cookie cookie = cookies[i];
107:         out.println("   " + cookie.getName() + " = " + cookie.getValue());
108:       }
109:       out.println();
110:     }
111:
112:     out.println("Request Is Secure: " + req.isSecure());
113:     out.println("Auth Type: " + req.getAuthType());
114:     out.println("HTTP Method: " + req.getMethod());
115:     out.println("Remote User: " + req.getRemoteUser());
116:     out.println("Request URI: " + req.getRequestURI());
117:     out.println("Context Path: " + req.getContextPath());
118:     out.println("Servlet Path: " + req.getServletPath());
119:     out.println("Path Info: " + req.getPathInfo());
120:     out.println("Path Trans: " + req.getPathTranslated());
121:     out.println("Query String: " + req.getQueryString());
122:     out.println();
123:
124:     HttpSession session = req.getSession();
125:     if (session != null) {
126:       out.println("Requested Session Id: " + req.getRequestedSessionId());
127:       out.println("Current Session Id: " + session.getId());
128:       out.println("Session Created Time: " + session.getCreationTime());
129:       out.println("Session Last Accessed Time: " + session.getLastAccessedTime());
130:       out.println("Session Timeout: " + session.getMaxInactiveInterval());
131:       out.println();
132:
133:       e = session.getAttributeNames();
134:       if (e != null) {
135:         out.println("Session values: ");
136:         while (e.hasMoreElements()) {
137:           String name = (String)e.nextElement();
138:           out.println("   " + name + " = " + session.getAttribute(name));
139:         }
140:       }
141:     }
142:
143:     out.flush();
144:     out.close();
145:   }
146:
147: } // end of class SnoopServlet.

 

»»» Top «««

 

5. Serve JuSP Pages

JudoScript function HTTP handling and Java servlets allows dynamic content be easily delivered via HTTP. You can quickly implement XML RPC calls, quick reporting, etc. But for more visually rich pages, it is common practice to separate logic from presentation HTML code. JSP (Java Server Page) is a popular solution for Java HTTP servers, as are other template approaches.

The JudoScript HTTP Server natively supports the JuSP (JudoScript Server Page) technology. Files with extension .jusp anywhere on the server are always run as JuSP. The following is a simplest JuSP page:

Listing 9. simple.jusp
1: <html><body>
2: <h2>Simple JuSP Page</h2>
3:
4: <p>The time is: <%= Date().fmtDate('yyyy-MM-dd hh:mm:ss') %></p>
5:
6: </body></html>

JuSP pages are like JSP (sans JSP tags); they use scriptlets (<% %> tags), embedded expressions (<%= %> tags) and occasionally directives (<!% %> tags) to embed JudoScript code. The programming model is also similar, and the embedded language is JudoScript. JuSP is simple but powerful to build full-featured web applications; please visit the JuSP Tutorial and Documentation for details. Let's take a look at another upload example, this time, in JuSP.

Listing 10. upload.jusp
 1: <html>
 2: <head><title>JuSP Upload</title>
 3: <body>
 4: <h2>JuSP Upload</h2>
 5: 
 6: <form method="POST" enctype="multipart/form-data"
 7:       action="upload.jusp">
 8: File name: <%!file 'filename1' %><br>
 9: File name: <%!file 'filename2' %><br>
10: Some text: <%!text 'foo' %><br>
11: <%!submit "Upload" %>
12: </form>
13: 
14: <%
15:   var uploads = request.getUploads();
16:   if uploads != null {
17:     %><pre><%
18:     for item in uploads {
19: %>
20: Uploaded Item:
21:   Field Name: <%= item.getFieldName() %>
22: Content Type: <%= item.getContentType() %>
23:    File Name: <%= item.getFileName() %>
24:    File Size: <%= item.getFileSize() %>
25: <%
26:       item.delete(); // we don't store it
27:     }
28:     %></pre><%
29:   }
30: 
31:   if form.foo {
32:     %><b>Extra text:</b> <%= form.foo %><br><%
33:   }
34: %>
35: </body></html>

 

»»» Top «««

 

6. Servlet API Conformity and JuSP Compatibility

One concern to work with a standard technology like J2EE is compability. It is always important to be sure that a servlet tested in one container works the same in another. Unfortunately, you, the programmer, are ultimately responsible for ensuring that your product works with different containers. The JudoScript HTTP server is based on the Tiny Java Web Server but is significantly modified for JudoScript's own built-in support. We will strive to keep it as consistent as possible with the Java Servlet API reference implementation, Tomcat.

 

»»» Top «««

 

7. Summary

The JudoScript HTTP server is a small but powerful general-purpose HTTP server. It is capable of serving both static and dynamic content. It supports Java servlet API, JudoScript Server Page and plain JudoScript function HTTP handling. It can be used as a test bed for Java servlets and JuSP pages; it can also be used to host fully functional web-based applications for reasonable load. Coupled with the power of JudoScript and Java in general, this provides a useful platform for more advanced, web-based tools.

A JudoScript HTTP server object is created by createHttpServer(), which may take a port number (default 8080) and a doc-root directory (default is the JudoScript current directory). The server object is configured programmatically; there is no configuration files to use. Once configured, call its start() method to start the HTTP service.

For servicing static content, you can call these methods to customize: addPathMapping(), addFileThrottle() and addRealm().

The JudoScript HTTP server reserves a special URI prefix, /judocall/, for invoking JudoScript functions residing in the same script that started the server; the name following that prefix is used as the HTTP handling function name. A HTTP handling function is invoked with three parameters, the request, response and the servlet object. What to do with it is totally up to the function implementation. To invoke HTTP handling functions without /judocall/, call the server object's addCallMapping() method to map a URI prefix to the function.

The JudoScript HTTP server supports Java servlets. Servlets must be first added to the server object via the addServlet() method, which takes a URI pattern, the servlet class name and optionally a set of servlet init parameters.

Last but not least, the JudoScript HTTP server has built-in support for JuSP pages. A URI with extension .jusp is interpreted as a JuSP request and handled accordingly. The JuSP pages are located the same way as static content.

 

»»» Top «««

 

8. Code Listing

  1. simplest_http_server.judo
  2. serve_static.judo
  3. judocall.judo
  4. snooper.judo
  5. upload.judo
  6. snoopy.judo
  7. SimpleServlet.java
  8. SnoopServlet.java
  9. simple.jusp
  10. upload.jusp


Copyright c 2001-2005 JudoScript.COM. All Rights Reserved.