| Previous | Table of Contents | Next |
There are a growing number of products that support servlets. In writing this book, we have used several providers. First, the Java Servlet Development Kit, or JSDK, provides a command-line program called servletrunner. This program loads servlets and handles servlet requests, but it does not support normal HTML requests, so it doesnt provide the same complete environment as a Web server. Second, the Java Web Server, a product provided by Sun, supports both servlets and JavaServer Pages, as discussed in later chapters. BEA WebLogic Application Server also supports servlets. All of these hosts support initialization arguments that the servlet acquires via the ServletConfig object.
Regardless of the host you pick, you need to know several things before running servlets:
servlet.adrotator.code=AdRotatorServlet
servlet.adrotator.initArgs=\
imagedir=c:\\temp\\chapter_07\\images
The Java Web Server provides excellent support for servlet programming. Even better is the fact that there is every indication that other major server vendors are adopting this technology as well. Expect support and additional functionality to appear in most server products. This means that if you write servlets now, you should see enhanced features and performance in the years to come at very little cost to you.
The servlet development kit is included on the CD-ROM that accompanies this book. Visit java.sun.com for information on downloading a trial version of the Java Web Server or check with your server provider to see if it supports servlets. You might also want to browse to www.livesoftware.com, a company that provides tools for adding servlet support to your existing Web server.
Perhaps one of the hardest aspects of programming servlets is testing themnot testing the features as much as the reliability. Consider that the servlet runs on the Web server and therefore is probably not accessible to a debugger. Certainly, some servlet hosts might provide debug capabilities, but it is not the norm. When an uncaught exception occurs in a servlet, it simply fails to return data. The programmer wont get any information from the client about the error beyond the nagging message, document contains no data. Also, many developers may write and test their servlets on one Web server and deploy them to another one, making it hard to expect normal debugging facilities. Finally, although servlets do have access to the log file, there are a number of issues with using log statements to debug the program.
The destination for the log is server dependent. Strings sent to the log may be altered and may have other log messages interspersed between them. Log-style debugging is also time consuming in the sense that you will often add logging comments to the code and then remove them for deployment. More important, the log is intended for administratively significant messages, not debugging. Despite these drawbacks, log-style debugging is perhaps the most portable and stable debugging mechanism for servlets.
To make log-style, or printf-style, debugging easier with servlets and other distributed programs, we have created a class listed in the code that follows; this code defines a class named DebugLog. The goal of this class is to provide multiple logging destinations, rather than just supporting the servlet log. In particular, the DebugLog object can be told to send log messages to a file, stream, or even to a log server that has been provided. This server prints messages to System.out or a file. In the case of the log server, you can watch log messages in real time while testing the servlet. Two methods are provided: one to log string messages and the other to log an exceptions stack trace. This can be especially useful when an exception occurs.
We also want to minimize the code changes required to move from development to production. The technique we use to accomplish this is a simple one. First, the DebugLog object is not initialized with a specific log destination. If no destination is provided, the logging code will simply ignore all logging messages. This means that you can leave debugging code in your servlets at the cost of a message send and an if statement. In the context of a server, this is a minimal requirement for reducing the maintenance required to remove debugging code.
The code for DebugLog follows. Notice that the main issues are keeping track of the server in a way that allows the server to go down and the logger to reconnect appropriately. In fact, if either the servlet goes away or the server goes down, the other will self-correct.
import java.io.*;
import java.net.*;
import LogServer;
public class DebugLog extends Object
{
private Socket server;
private String serverName;
private BufferedReader serverReader;
private PrintWriter log;
private static DebugLog sharedLog;
public static synchronized DebugLog getSharedLog()
{
if(sharedLog == null) sharedLog = new DebugLog();
return sharedLog;
}
public DebugLog()
{
log = null;
}
public synchronized boolean initialized()
{
return ((log != null)||(serverName!=null));
}
public synchronized void log(String str)
{
//exit quick if no log
if((log != null)||(serverName!=null))
{
log(str,true);//retry
}
}
public synchronized void log(Exception exp)
{
if((log != null)||(serverName!=null))
{
StringWriter out;
PrintWriter printOut;
String logTrace;
StringReader in;
BufferedReader bufIn;
String curLine;
try
{
out = new StringWriter();
printOut = new PrintWriter(out);
exp.printStackTrace(printOut);
printOut.close();
logTrace = out.toString();
in = new StringReader(logTrace);
bufIn = new BufferedReader(in);
while((curLine = bufIn.readLine()) != null)
{
log(curLine,true);
}
bufIn.close();
}
catch(Exception ex)
{
}
}
}
//protected method that allows the logger to reconnect
//to a server
protected synchronized void log(String str,boolean retry)
{
boolean error=false;
if(log != null)//exit quick if no log
{
try
{
log.println(str);
if(serverReader != null)
{
//Read the response, but ignore
//This should force an exception
// if the socket is closed
serverReader.readLine();
}
}
catch(Exception ex)
{
error = true;
closeLog();
}
}
else
{
error = true;
}
if((serverName != null) && error)
{
if(retry)
{
logTo(serverName);
log(str,false);//only retry one time
}
else
{
closeLog();
}
}
}
public synchronized void logTo(File f)
{
if(f!=null)
{
closeLog();
try
{
FileWriter fileIn = new
FileWriter(f.getAbsolutePath(),true);
log = new PrintWriter(fileIn,true);
}
catch(Exception exp)
{
log = null;
}
}
}
public synchronized void logTo(OutputStream stream)
{
if(stream!=null)
{
closeLog();
try
{
log = new PrintWriter(stream,true);
}
catch(Exception exp)
{
log = null;
}
}
}
public synchronized void logTo(String logServer)
{
if(logServer!=null)
{
closeLog();
serverName = logServer;
try
{
server = new
Socket(logServer,LogServer.DEFAULT_PORT);
InputStreamReader readIn;
readIn = new
InputStreamReader(server.getInputStream());
serverReader = new BufferedReader(readIn);
log = new
PrintWriter(server.getOutputStream(),true);
server.setSoTimeout(2000);//two seconds
}
catch(Exception exp)
{
log = null;
server = null;
}
}
}
public synchronized void closeLog()
{
if((log!=null)&&(server!=null))
{
try
{
log.println(LogServer.DISCONNECT_MSG);
}
catch(Exception exp)
{
}
}
if(log != null)
{
log.flush();
log.close();
log = null;
}
if(server != null)
{
try
{
if(serverReader != null) serverReader.close();
server.close();
server = null;
serverReader = null;
}
catch(Exception ex)
{
}
}
}
public synchronized void finalize()
{
closeLog();
}
}
class DebugLogTester
{
public static int MSG_COUNT=100;
public static void main(String[] args)
{
DebugLog logger = DebugLog.getSharedLog();
int i;
System.out.println(Created log.);
logger.logTo(192.168.0.172);
System.out.println(Set log dest.);
System.out.println(Logging messages.);
for(i=0;i<MSG_COUNT;i++)
{
try
{
logger.log(Test +i);
Thread.sleep(100);
}
catch(Exception exp)
{
System.out.println(Exception: +exp);
}
}
System.out.println(Closing log.);
logger.closeLog();
}
}
| Previous | Table of Contents | Next |