|
Benjy's Guide for
Constructing CD-Based Dynamic Web Sites
|
Additional notes on this technique
Notes on using Jetty-6 with
this cookbook
Introduction
Running a static web site
from a CD-ROM is an easy thing to do. All you need are the HTML files
and a web browser. Static web sites on a CD are a good way to
distribute information to those that do not have Internet access or
have slow-speed lines. But, dynamic sites, those whose content changes
based on user input,
can provide features that static pages cannot. However, running a
dynamic web site requires a web server.
It is possible to distribute dynamic web content and a server on a CD
and require the user to install the web server on his machine.
This document describes a less invasive approach using Java-only tools
that requires no components to be installed on the target user's
machine. In the approach described here, the CD can be configured to
autorun the server and site directly from the CD. This description
addresses the
Windows operating system; however, this approach can be adapted to
other platforms such as Linux and Mac OS/X. All the code is portable
except for code to autorun the web server and the method I describe to
start the default web browser automatically.
It should be noted that applets can also be used to deliver dynamic
content; however, it might be necessary to update the end users' browsers
with a Java plug-in. However, using the approach described here, no
installations are required.
Motivation
What types of information can
be distributed using the technique? Some examples are
- Marketing information
- Teaching materials
- Decision Support Systems (DSS) or other scientific tools
In the example of marketing
information, large amounts of product information are best organized in
databases where pricing and other information can be easily
changed. The product information in the site can be changed by
updating a database and creating a new CD for salesmen or for
customer mailings.
Teaching materials can benefit from having quizzes that guide a
student's progress through a web site. Teaching aids, such as
small simulations, can also be included in a dynamic site.
Whole scientific applications can be distributed using a dynamic web
site on a CD. The power of this approach is to take advantage of
the universal interface created by HTML and distribute the application
in a platform independent manner.
Java Tools
I selected a Java approach for
creating CD-based dynamic web sites server for several reasons.
First, a Java servlet server with Java Server Pages (JSP) provides a
powerful web server including scripting features, the ability to
produce custom tags, and the power of servlets. JDBC (Java DataBase
Connectivity) provides the
ability to connect web applications to common databases, and a
Java-only database is available.
Second, an all Java solution is portable and can thus be run on a
number of platforms including Unix and Windows operating systems.
And finally, according to my understanding, the license
agreements for the tools described here allow
for them to be embedded in commercial products.
The tools are Sun's
Java Runtime Environment (JRE)
(java.sun.com), the MortBay Jetty servlet
server (www.mortbay.org), the JSP
standard tag library JSTL
(jakarta.apache.org/taglibs/),
and the hsqldb database
engine (hsqldb.sourceforge.net).
I make no claims on the license agreements
associated with these packages. You should consult the indivdual
licenses for these tools to confirm that they can be freely
redistributed and under what circumstances.
Figure 1 - a sample site, click to enlarge
There are other approaches and other tools that can be used to
construct a CD-based web site, as well as variations on this approach.
The reader can experiment and expand on the ideas presented here.
Dynamic Web Server CD Cookbook
This is a cookbook approach
for creating a dynamic web server that runs from a CD. The CD
includes the Jetty servlet server, a Java virtual machine (JVM), and
autorun files. Optionally, a database package and a database can
be added.
Here is a quick overview of the steps needed to create a CD-based dynamic
web site. For complicated steps, a link is given for more details.
- Install Java:
Install a Java Development Kit (JDK) from
java.sun.com that
also includes a JRE. The JDK will be used for
developing the site, and the JRE will be placed on the CD to run the
web server.
- Create a CD image directory:
Create an image of your site on your hard drive. Begin by creating a
subdirectory called CDRoot.
- Put a copy of the JRE in the CDRoot:
Locate the JRE you installed in the first step, and copy it to CDRoot.
I recently installed a Java 1.5 JDK under Windows, and the JRE was installed in
C:\Program Files\Java\jre1.5.0_03. I copied this subdirectory to CDRoot.
- Install Jetty:
Download the Jetty servlet server zip file from
www.mortbay.org. Install Jetty by extracting the zip file into
CDRoot.
- Configure Jetty:
Copy the jetty.xml in the Jetty etc directory to cd.xml, change the
HTTP port from 8080 to 80, and disable logging. (Logging is disabled so
that no attempt is made to write log files to the CD when the server is
run.) Also, remove all .war (web archive) files from the
webapps subdirectory.
- Install the JSP JSTL standard taglib:
(Optional). Download the JSTL taglib zip file from
jakarta.apache.org/taglibs/.
Follow the
Standard-1.1 JSTL (2.0) link to the zip file, download it, extract it
into a temporary
location, and copy the lib/standard.jar and lib/jstl.jar files to the Jetty
ext directory. You will only need these files if you plan to use JSP
and use the standard taglibs.
- Install the HSQLDB database package:
(Optional). Download the HSQLDB database package from
hsqldb.sourceforge.net
and extract it
to some directory outside the CDRoot subtree. Copy the lib/hsqldb.jar
library to the Jetty ext directory. This step is only required if you
plan to use a database.
- Create autorun files:
Create autorun files, run.bat and autorun.inf, in CDRoot so that
the web server
will be executed when the CD is read on a PC. Follow the link for
examples of these files. CDRoot should
look something like this
- Create utility servlets:
Create servlets to
- Launch the default browser when the servlet server becomes active.
- Provide a way to shutdown the servlet server from a web form.
- Develop databases:
(Optional). Use HSQLDB to create databases for your site. Create
a database directory under Jetty and put your databases there. Edit the
properties file for each database to make it read only.
- Develop your site:
Create a web application using servlets and/or JSP pages. Test your site
using the hard drive image of the CD.
- Precompile and install JSP pages:
(Optional).If you use JSP, precompile the pages, copy the class files
to your web application, and modify your web.xml to include entries
for each page.
- Create a CD:
Before burning a CD, make sure all JSP files are precompiled and that
the web.xml file for your web app has entries for all of them. Make sure
the read-only attribute is set to true in each databases .property file.
Make sure the pages in your site work properly.
Create a CD by copying the files in CDRoot to the top level of the CD.
Notes
Perhaps the biggest problem with this approach is the potential delay
between running a CD that starts Jetty and when the dynamic site becomes
available. An improvement over the browser launch servlet I've provided
would be to start a small GUI control program in one thread and start
Jetty in another. The GUI process would give the user quicker feedback
and give a more application-like control panel over my shutdown applet.
Some systems are configured not to autorun a CD, so the users of your
CD site should be told to double click on run.bat if the CD doesn't
automatically load Jetty. You can also give them the URL of your site,
e.g., http://localhost/CDServlet, in case there are problems launching
the default browser. If your users typically use your site often,
you could include instructions for copying the entire CD to their
hard drive to avoid CD-ROM delays each time the site is run.
I would appreciate receiving questions and suggestions about this document
at the e-mail address at the bottom of the page. If there is enough
interest, I'll create an FAQ.
More information for selected steps
Configure Jetty
Copy <JETTY>/etc/jetty.xml to <JETTY>/etc/cd.xml where
"<JETTY>" is the Jetty directory under CDRoot. If Jetty 5.1.3
were installed, <JETTY> would be Jetty-5.1.3. Edit cd.xml and make two
changes. The first disables logging.
Find the request log section of the configuration file:
<!--===============================================================
-->
<!-- Configure the Request Log-->
<!--===============================================================
-->
<Set name="RequestLog">
[snip]
</Set>
Add HTML comment lines to
disable the entire <Set>....</Set> section:
<!--===============================================================
-->
<!-- Configure the Request Log-->
<!--===============================================================
-->
<!--
<Set name="RequestLog">
[snip]
</Set>
-->
The second change is optional and involves changing the default port
for Jetty from 8080 to 80. Locate the configuration line that sets
the "jetty.port" system property to 8080 and change "default=8080" to
"default=80". Or, you can change the run.bat autorun file so that the
javaw command contains "-Djetty.port=80", which overrides the default
port specified in the config file.
I recommend removing
all the files in the <Jetty>/webapps directory. War files,
*.war, are unpacked each time Jetty is run. This delays startup
and won't work without extra work on a CD-ROM-based servlet server.
Autorun files
If you want the CD to autorun under Windows, create run.bat and autorun.info.
Run.bat looks something like
ECHO OFF
cd jetty-5.1.3
start ..\jre1.5.0_03\bin\javaw -jar start.jar etc/cd.xml
And the autorun.inf to invoke
run.bat follows:
Note that javaw is used to run the server without a shell window.
See the updated version of run.bat
that works with the Jetty 6 version
of the stop servlet.
Utility servlets
I find it useful to have the default browser automatically started
after Jetty is up and running. (The Jetty startup time
depends on the speed of the machine being used and the speed of its
CD drive. Starting the browser prematurely with a URL served by the servlet
server will give a "connection refused"
message.) One way to have Jetty start the browser is to create a
subclass of ServletContextListener that is run when your servlet
context is up and can be used to start the default browser with a
URL. Here is a version that works under Windows 2000 and
should work under NT and XP:
package
com.benjysbrain.servlet ;
import javax.servlet.* ;
import javax.servlet.http.* ;
// Copyright (c) 2005 by Ben E. Cline. All rights reserved.
/**
Launch a browser when the servlet server is up.
*/
public class AppInit implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
ServletContext application =
sce.getServletContext() ;
String startURL =
application.getInitParameter("startURL") ;
// Do a "start <startURL>" command.
try {
Runtime runtime =
Runtime.getRuntime() ;
runtime.exec("cmd.exe /c
start " + startURL) ;
}
catch(Exception e) {
System.out.println(e) ;
}
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
The "start" command is used to run
the web browser in a new process. Because it is a
built-in command in Windows 2000, "cmd.exe" must be run instead of
running "start" directly. The URL that the browser is started on
is taken from the web.xml file for the web application. An excerpt
of a web.xml file that relates to this servlet follows.
See the sample web.xml for a complete web app
web.xml file.
The "<context-param>" and "<listener>" tags configure the
ServletContextListener.
<context-param>
<param-name>startURL</param-name>
<param-value>http://localhost/CDServlet/</param-value>
</context-param>
<listener>
<listener-class>com.benjysbrain.servlet.AppInit</listener-class>
</listener>
Because Jetty is run in the
background, I created a servlet, called stop, that can be called from a
web page to stop the server. When the server starts, I use the AppInit
servlet to invoke index.html in the top-level directory of my web app.
This page contains a form to run the stop servlet:
<TITLE>Welcome</TITLE>
<script language="JavaScript">
window.open("/CDServlet/demo", "Welcome", '') ;
self.resizeTo(200, 200) ;
</script>
<p>
<form action=/CDServlet/stop method=post>
<input type=submit value="Stop Server">
</form>
This page first opens a new
browser window to run my primary servlet and then resizes itself to a
small window with a "Stop Server" button. Pressing the button
stops Jetty. See Figure 1.
This approach does have some drawbacks. For example, if the user closes
the larger browser window first, his default browser size will, in most
cases, become the size of the smaller window with the stop button. Also,
browsers that prevent popup windows will prevent the larger window
from opening.
The source code for the stop servlet follows.
See my
notes on a version of the stop
servlet that should work for both Jetty 5 and Jetty 6.
package com.benjysbrain.servlet ;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.* ;
import java.util.* ;
import org.mortbay.http.* ;
// Copyright (c) 2005 by Ben E. Cline. All rights reserved.
/**
Shutdown Jetty.
*/
public class stop extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
response.setContentType("text/html") ;
PrintWriter out = response.getWriter() ;
out.println("<HTML><HEAD><TITLE>Stop</TITLE></HEAD><BODY>")
;
out.println("<h2>Stopping
Jetty</h2>") ;
out.println("</body></html>") ;
out.close() ;
Collection servers =
HttpServer.getHttpServers() ;
Iterator sList = servers.iterator() ;
while(sList.hasNext()) {
HttpServer s = (HttpServer) sList.next() ;
try {
s.stop() ;
}
catch(Exception e) {
System.out.println("Stop: "
+ e) ;
out.println("Problem
stopping: " + e) ;
}
}
}
}
The web.xml configuration for stop
is
<servlet>
<servlet-name>stop</servlet-name>
<servlet-class>com.benjysbrain.servlet.stop</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>stop</servlet-name>
<url-pattern>/stop/*</url-pattern>
</servlet-mapping>
Develop database
Instructions on creating databases is beyond the scope of this page, but
I've included a few notes to help you get started. The HSQLDB package
comes with an extensive manual, which you should find useful.
I first create a development directory outside the CDRoot
subtree where I keep the databases and a shell script to run the
database server. I create a file of SQL commands called db.sql to
build the database. Next, I run HSQLDB in server mode using a command
similar to
java -cp ..\lib\hsqldb.jar org.hsqldb.Server -database.0 BenjysProjects
This will create a new database called BenjysProjects or open an existing
database by the same name using a proprietary protocol.
Next I make a copy of the demo/org/hsqldb/sample/sqltool.rc file in my
development directory. I add the following lines to indicate that id
"cdsite" refers to the server on localhost using id "sa" with no
password.
urlid cdsite
url jdbc:hsqldb:hsql://localhost
username sa
password
This command runs sqltool and creates the database by executing the
SQL statements in sql.db.
java -jar ..\lib\hsqldb.jar --rcfile sqltool.rc cdsite sql.db
You can use
java -jar ..\lib\hsqldb.jar --rcfile sqltool.rc cdsite
to query and update the database. When I am finished creating the database,
I copy all the database files to the Jetty databases directory,
which I created, and edit the database properties file, setting
readonly=true.
Here is sample Java to open the database from a Jetty servlet. Once the
database connection is open,
standard JDBC calls can be made to query the database.
Connection c = null ;
try {
Class.forName("org.hsqldb.jdbcDriver" );
c =
DriverManager.getConnection("jdbc:hsqldb:file:databases/BenjysProjects",
"sa", "");
}
catch(Exception e) {
System.out.println("DB
Open: " + e) ;
}
If you are using
the standard JSP taglib, you can connect to the database using
<sql:setDataSource
var="db"
driver="org.hsqldb.jdbcDriver"
url="jdbc:hsqldb:file:databases/BenjysProjects"
/>
Developing your site
You need to decide if you're going to use servlets, JSP, or a
combination of both and if your application requires a database. I suggest
you create your site as a web app. A web app consists of a
directory in the <Jetty>/webapps directory. The
subdirectory contains HTML files and/or JSP files, e.g., index.html,
and a WEB-INF subdirectory. This subdirectory contains a web.xml
file, a classes subdirectory for class files, and a lib directory for
library files. The web.xml file contains configuration
information for the web app. I will address JSP pages in a later
section. For servlets, the web.xml file maps URL's to servlets
and specifies the class files of the servlets. See the example
web.xml file.
During development, I use the JDK by placing the JDK bin directory in my
path. I then use it to develop servlets and JSP files. The JDK is required
to compile servlets and JSP files. To run Jetty with the JDK in the path,
go the Jetty directory and run
java -jar start.jar etc/cd.xml
You can test your site by then running Jetty with the JRE using the run.bat
file in the CDRoot directory.
Precompiling JSP
JSP pages are a combination of
Java code, html, standard tag calls, and custom tag calls. During
development, the programmer creates a text file with a .jsp
extension. When the page is visited by a browser, Jetty and
Jasper, a JSP engine, decides if there is up to date
executable code to generate the page for the browser. If not,
Jasper first converts the .jsp program into a .java file.
It then compiles this program into
a .class file and runs it as a servlet. The generated .java and
.class files are kept in the Jetty temporary subdirectory in the Jetty
home directory. It is work/<webapp-port>/org/apache/jsp/ by
default where <webapp-port> is the string
"Jetty__<n>__<appname>", with <n> being the port
number of the server, e.g., 80, and <appname> being the name of
the web app.
Under Jetty-6 and XP,
temporary disk space is used, so you do not need to precompile
to test a CD. But you should precompile JSPs for production CDs for
better performance.
Before creating a CD image, all the JSP files must be visited so that
they will be translated and compiled. Unfortunately, copying the
"work" directory to CD with all the JSP files translated won't work
because Jetty will not use a read-only temporary directory.
There are at least three ways to precompile the JSP files. All three
methods require that several lines be added to the web app's web.xml
file for each JSP file. The methods are
- Simple method
- Directly using JspC.
- Using "ant".
If you only have a few JSP files, the simple method
should work fine:
- Copy the org subdirectory from
work/<webapp-port> directory described above to your web app's
classes directory, or jar up this directory and put it in your web
app's lib subdirectory.
- For each JSP file, add a servlet
entry to the web.xml file for your web app. For example, adding
the db.jsp page would use the following entry:
<servlet>
<servlet-name>db</servlet-name>
<servlet-class>org.apache.jsp.db_jsp</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>db</servlet-name>
<url-pattern>/db.jsp/*</url-pattern>
</servlet-mapping>
The second method invokes org.apache.jasper.JspC directly. JspC will translate
and compile all the .jsp files into .class files for servlets and optionally
generate the lines needed for your web.xml file. In order to run JspC
directly, you need the following files in your class path:
- From the Jetty ext subdirectory
- jasper-compiler.jar
- jasper-runtime.jar
- commons-loggins.jar
- commons-el.jar (If you use the JSP expression language)
- standard.jar (If you use JSTL)
- jstl.jar (If you use JSTL)
- From the Jetty lib subdirectory
- javax.servlet.jar
- ant.jar
- From the JDK lib subdirectory
Under Jetty-6, the jar files you need are different. My Jetty-6 page
contains a list of Jetty 6 jar files
needed for precompiling JSP files.
I create a development directory and copy all the .jsp files there.
I then run JspC in the development directory:
java org.apache.jasper.JspC -d . -l -s -uriroot <webapps-dir> -compile -webxml webfrag.xml *.jsp
where <webapps-dir> is the full path to the directory for your web app
under the Jetty webapps directory. "-d ." indicates the files are in the
current directory. "-l and -s" output each filename as it succeeds or fails.
"-uniroot" (or -webapp) is the webapp directory for your web app. "-webxml"
gives the filename for a file to receive the web.xml fragment for the
.jsp files that are processed.
Once you run JspC successfully, copy the "org" subdirectory to your
WEB-INF/classes directory in your web app or jar this subdirectory and
place it in the WEB-INF/lib subdirectory. Insert the lines in the
-webxml file into the web.xml file.
The final method for precompiling your JSP files is to use "ant", the
Java-based replacement for the "make" utility. Do a web search for details.
The sample web.xml file
<?xml version="1.0"
encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.cm/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>Welcome to Benjy's Brain Demo
Servlet</display-name>
<description>
Demo Servlet for CD-based Web Server.
</description>
<servlet>
<servlet-name>DemoServlet</servlet-name>
<servlet-class>com.benjysbrain.servlet.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DemoServlet</servlet-name>
<url-pattern>/demo/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>stop</servlet-name>
<servlet-class>com.benjysbrain.servlet.stop</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>stop</servlet-name>
<url-pattern>/stop/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>startURL</param-name>
<param-value>http://localhost/CDServlet/</param-value>
</context-param>
<listener>
<listener-class>com.benjysbrain.servlet.AppInit</listener-class>
</listener>
</web-app>
This site © copyright
2005 by Ben E. Cline. E-Mail: