JudoScript HTTP Server with Servlet and JuSP SupportBy James Jianbo Huang December 2005 non-printer versionAbstract
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.
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:
- Serves static content
- Handles HTTP requests with plain JudoScript functions
- Serves JuSP pages
- 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 «««
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.
In the above listing, line 7 sets a realm for /src. This means
that content in that directory are password protected.
»»» Top «««
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.
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.
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 «««
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 «««
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 «««
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 «««
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 «««
- simplest_http_server.judo
- serve_static.judo
- judocall.judo
- snooper.judo
- upload.judo
- snoopy.judo
- SimpleServlet.java
- SnoopServlet.java
- simple.jusp
- upload.jusp
|