This chapter describes a generic handler servlet, which can be extended to define bean-independent handlers. An example is provided. In addition, an error handling mechanism is built on top of the logging framework.
The previous chapter described a JSP-based template for handlers. You had to replace two variables (bean_name and bean_id) to build a JSP handler for a particular type of data beans. This works fine especially when you must customize the handlers. Most of the examples presented in this guide use customized versions of the JSP handler template. However, if you have a few forms whose handlers are very similar you might consider moving the Java code into a utility class like this:
package com.devsphere.tests.mapping.handler;
import com.devsphere.mapping.*;
import com.devsphere.logging.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SimpleHandler {
public static void handle(
HttpServletRequest request,
HttpServletResponse response,
java.io.Writer out,
ServletContext application,
Object beanObject)
throws ServletException, java.io.IOException {
// Get the bean resources
java.util.ResourceBundle beanRes
= HandlerUtils.getBeanResources(beanObject.getClass());
// Construct the base path
String basePath = request.getServletPath();
int slashIndex = basePath.lastIndexOf('/');
basePath = slashIndex != -1 ? basePath.substring(0, slashIndex+1) : "";
// Determine the HTTP method
boolean isPostMethod = request.getMethod().equals("POST");
// Create a logger that wraps the servlet context
ServletLogger logger = new ServletLogger(application);
// Wrap the form data
FormData formData = new ServletFormData(request);
// Form-to-bean mapping: request parameters are mapped to bean properties
java.util.Hashtable errorTable = FormUtils.formToBean(formData, beanObject, logger);
if (isPostMethod && errorTable == null) {
// Construct the processor's path
String procPath = basePath + beanRes.getString("[PROC_NAME]").trim();
// Process the valid data bean instance
application.getRequestDispatcher(procPath).forward(request, response);
} else {
if (!isPostMethod)
// Ignore the user errors if the form is requested with GET.
errorTable = HandlerUtils.removeUserErrors(errorTable);
// Construct the form's path
String formPath = basePath + beanRes.getString("[FORM_NAME]").trim();
formPath = application.getRealPath(formPath);
// Get the form template
FormTemplate template = FormUtils.getTemplate(new java.io.File(formPath));
// Get a new document
FormDocument document = template.getDocument();
// Bean-to-form mapping: bean properties are mapped to form elements
FormUtils.beanToForm(beanObject, errorTable, document, logger);
// Send the form document
document.send(out);
}
}
}
Then, two JSP handlers could look like this:
<%@ page language="java" import="com.devsphere.tests.mapping.handler.SimpleHandler" %>
<jsp:useBean id="firstBean" class="first.FirstBean" scope="request"/>
<% SimpleHandler.handle(request, response, out, application, firstBean); %>
<%@ page language="java" import="com.devsphere.tests.mapping.handler.SimpleHandler" %>
<jsp:useBean id="secondBean" class="second.SecondBean" scope="request"/>
<% SimpleHandler.handle(request, response, out, application, secondBean); %>
The SimpleHandler class would simplify both the creation and the maintenance of the JSP handlers. However, it is neither customizable nor extensible. For example, you don't have access to the FormDocument object that allows the insertion of HTML content dynamically using the setValue() method. Also you might have to replace the handle() method with handle1() and handle2() if the two JSP handlers must work in slightly different ways.
The GenericHandler servlet is much better than SimpleHandler:
- It can be customized using init parameters. The name of the bean class is taken from the BEAN_NAME parameter. As you'll see in the next section of this chapter, the same servlet class may be used to define multiple servles. The servlet engine will create one instance of the servlet class for each defined servlet. This way you don't have to write a handler for each bean.
- It is extensible. You have to extend GenericHandler in order to define the process() method. This way you can build specialized handlers that remain bean-independent. In addition, you may override the createBeanInstance() method to get access to the bean object before it is used by the mapping utilities. Overriding createDocument() or handle() you get access to the FormDocument object before or after it is filled with error messages and default values. You may also override isValidBean() and handleInvalidBean() to do application-specific validation.
- It is faster than a JSP handler is. The servlet gets the bean class and the bean resources only once during its initialization. The JSP does this for each HTTP request within its service() method. Of course, the bean class is cached by some class loader and the bean resource bundle is cached by java.util.ResourceBundle, but the searching of a large cache may be an expensive operation.
The GenericHandler servlet also uses a better error handling mechanism based on com.devsphere.helpers.logging.ServletErrorHandler. The error messages are stored in a localizable resource bundle: ServletResources.properties. All these tasks seem easy using the logging helpers described later in this chapter.
The request and response objects are wrapped in a HandlingContext instance, whose getFormData() method returns the ServletFormData instance used by FormUtils.formToBean(). This enhances the extensibility of the generic servlet. For example, you may extend the HandlingContext class so that its getFormData() method returns your own implementation of FormData (instead of ServletFormData). You could also add new methods to the new handling context class. When you override GenericHandler to define the process() method, you would have to override the createHandlingContext() method to return an instance of you own handling context class. The GenericHandler's handle() method would pass the new handling context instance to your process() method.
The servlet has only one minor disadvantage: its configuration needs more work than the deployment of a JSP handler. You must ensure that the CLASSPATH is configured properly and you must define the servlet within some config files or use an admin utility of the servlet engine. The BEAN_NAME init parameter must contain the name of the bean class and the value of BASE_PATH must be the virtual folder of the HTML form.
GenericHandler.java:
package com.devsphere.helpers.mapping;
import com.devsphere.helpers.logging.*;
import com.devsphere.mapping.*;
import com.devsphere.logging.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
/**
* Generic handler servlet
*
* Init parameters:
* BEAN_NAME - the name of the bean class
* BASE_PATH - the virtual folder of the HTML form
* ALLOW_MULTIPART - if true, the use of "multipart/form-data" is allowed
* LOG_SOCKET_EXCEPTION - if true, any java.net.SocketException is logged
*
* Warning! Do not call any method of this class except the "resource utils".
*
* You may override any of the methods to add new functionality,
* but study their use first and make sure that the handling mechanism
* continues to work correctly.
*/
public abstract class GenericHandler extends HttpServlet {
public static final String SERVLET_RESOURCES
= "com.devsphere.helpers.mapping.ServletResources";
public static final String BEAN_NAME_PARAM = "BEAN_NAME";
public static final String BASE_PATH_PARAM = "BASE_PATH";
public static final String ALLOW_MULTIPART_PARAM = "ALLOW_MULTIPART";
public static final String LOG_SOCKET_EXCEPTION_PARAM = "LOG_SOCKET_EXCEPTION";
public static final boolean DEFAULT_ALLOW_MULTIPART = false;
public static final boolean DEFAULT_LOG_SOCKET_EXCEPTION = false;
public static final String FORM_NAME_KEY = "[FORM_NAME]";
// Servlet state
protected Exception initError = null;
protected AbstractLogger logger;
protected ResourceBundle servletRes;
protected ServletErrorHandler errorHandler;
protected String beanName;
protected Class beanClass;
protected ResourceBundle beanRes;
protected String basePath;
protected String formName;
protected boolean allowMultipart;
protected boolean logSocketException;
//* INITIALIZATION
/**
* Initializes the servlet
*/
public void init(ServletConfig config) throws ServletException {
try {
super.init(config);
initialize();
} catch (ServletException e) {
initError = e;
} catch (Exception t) {
// It must be a runtime exception that wasn't logged yet.
// errorHandler might not be available
initError = t;
if (logger != null)
logger.log(t);
else
t.printStackTrace();
}
}
/**
* Initializes the servlet
*/
protected void initialize() throws ServletException {
// Create the logger
logger = createLogger();
// Get the servlet resources
try {
servletRes = ResourceBundle.getBundle(SERVLET_RESOURCES);
} catch (MissingResourceException e) {
logger.log(e); // errorHandler isn't available
throw new ServletException(e.getMessage());
}
// Create the error handler
errorHandler = createErrorHandler();
// Get the bean name
beanName = getInitParameter(BEAN_NAME_PARAM);
if (beanName == null || beanName.length() == 0)
errorHandler.fatalError("[MISSING_INIT_PARAM]", null, BEAN_NAME_PARAM);
// Get the bean class
try {
beanClass = Class.forName(beanName);
} catch (Exception t) {
errorHandler.fatalError("[COULDNT_LOAD_BEAN_CLASS]", t, beanName);
}
// Get the bean resources
try {
beanRes = HandlerUtils.getBeanResources(beanName);
} catch (Exception t) {
errorHandler.fatalError("[COULDNT_LOAD_BEAN_RESOURCES]", t, beanName);
}
// Get the base path
basePath = getInitParameter(BASE_PATH_PARAM);
if (basePath == null || basePath.length() == 0)
errorHandler.fatalError("[MISSING_INIT_PARAM]", null, BASE_PATH_PARAM);
if (!basePath.endsWith("/") && !basePath.endsWith(File.separator))
basePath += "/";
// Get the allowMultipart flag
allowMultipart = DEFAULT_ALLOW_MULTIPART;
String amParam = getInitParameter(ALLOW_MULTIPART_PARAM);
if (amParam != null)
allowMultipart = Boolean.valueOf(amParam).booleanValue();
// Get the logSocketException flag
logSocketException = DEFAULT_LOG_SOCKET_EXCEPTION;
String lseParam = getInitParameter(LOG_SOCKET_EXCEPTION_PARAM);
if (lseParam != null)
logSocketException = Boolean.valueOf(lseParam).booleanValue();
// Get the form name
formName = getBeanResourceAsString(FORM_NAME_KEY, false).trim();
}
/**
* Creates a logger that wraps the servlet context
*/
protected AbstractLogger createLogger() throws ServletException {
return new ServletLogger(getServletContext());
}
/**
* Creates the error handler
*/
protected ServletErrorHandler createErrorHandler() throws ServletException {
return new ServletErrorHandler(servletRes, logger);
}
//* ABSTRACT PROCESSING METHOD
/**
* Subclasses must override this method
* so that they can process the valid bean objects
*/
protected abstract void process(HandlingContext handlingContext,
Object beanObject) throws ServletException, IOException;
//* REQUEST HANDLING
/**
* Creates a bean instance. Override this method to get access
* to the bean object before it is used by the mapping utilities.
*/
protected Object createBeanInstance() throws ServletException {
Object beanObject = null;
try {
beanObject = beanClass.newInstance();
} catch (Exception t) {
errorHandler.fatalError("[COULDNT_INSTANTIATE_BEAN_CLASS]", t,
beanName);
}
return beanObject;
}
/**
* This method may be overridden to validate the bean objects just
* before the processing. If the returned value is false the process()
* method isn't called anymore and the form will be returned to the user.
* In addition, the handleInvalidBean() is called before the bean object
* is mapped to the form document.
* In the current implementation, this method always returns true.
*/
protected boolean isValidBean(Object beanObject) {
return true;
}
/**
* This method may be overridden to signal errors to the user.
* It is called only if isValidBean() returns false.
* In the current implementation, this method does nothing.
*/
protected void handleInvalidBean(Object beanObject, FormDocument document)
throws ServletException {
}
/**
* Gets a file path passing BASE_PATH+'/'+fileName to
* getServletContext().getRealPath(). If getRealPath() returns null,
* this method tries to convert BASE_PATH+'/'+fileName to a system path
* replacing all '/' with File.separatorChar.
*/
protected String getFilePath(String fileName) {
String path = basePath + fileName;
String realPath = getServletContext().getRealPath(path);
if (realPath != null)
return realPath;
else
return path.replace('/', File.separatorChar);
}
/**
* Creates a new form document. Overriding this method, you get access
* to the FormDocument object before it is filled with error messages
* and default values.
*/
protected FormDocument createDocument()
throws ServletException, IOException {
// Get the form template
String formPath = getFilePath(formName);
File file = new File(formPath);
if (!file.exists())
errorHandler.fatalError("[HTML_FORM_NOT_FOUND]", null,
formPath, BASE_PATH_PARAM, FORM_NAME_KEY);
FormTemplate template = FormUtils.getTemplate(file);
// Return a new document
return template.getDocument();
}
/**
* Creates the handling context.
*/
protected HandlingContext createHandlingContext(HttpServletRequest request,
HttpServletResponse response, boolean postMethod) throws IOException {
return new HandlingContext(request, response, postMethod, allowMultipart);
}
/**
* Handles a HTTP request and returns a FormDocument that must be sent
* to the user or null if the HTML content was already sent.
* Overriding this method, you get access to the FormDocument object
* after it is filled with error messages and default values.
*/
protected FormDocument handle(HandlingContext handlingContext)
throws ServletException, IOException {
// Create a bean instance
Object beanObject = createBeanInstance();
// Get the form data
FormData formData = handlingContext.getFormData();
// Form-to-bean mapping: request parameters are mapped to bean properties
Hashtable errorTable = FormUtils.formToBean(formData, beanObject, logger);
boolean postMethod = handlingContext.isPostMethod();
if (postMethod && errorTable == null && isValidBean(beanObject)) {
// Process the valid bean
process(handlingContext, beanObject);
return null;
} else {
if (!postMethod)
// Ignore the user errors if the form is requested with GET.
errorTable = HandlerUtils.removeUserErrors(errorTable);
// Get the form document
FormDocument document = createDocument();
if (postMethod && errorTable == null)
// Handle the invalid bean object
handleInvalidBean(beanObject, document);
// Bean-to-form mapping: bean properties are mapped to form elements
FormUtils.beanToForm(beanObject, errorTable, document, logger);
// Return the form document
return document;
}
}
/**
* Handles a HTTP request. Called by doGet() and doPost()
*/
protected void doGetOrPost(HttpServletRequest request,
HttpServletResponse response, boolean postMethod) {
Exception error = initError;
if (error == null) {
FormDocument document = null;
try {
// Create the handling context
HandlingContext handlingContext = createHandlingContext(
request, response, postMethod);
// Call the real implementation of the request handling
document = handle(handlingContext);
} catch (ServletException e) {
// Already handled error
error = e;
} catch (Exception t) {
if (!logSocketException && t instanceof java.net.SocketException)
// Some network error occurred or, more likely,
// the user clicked the 'Stop' button of the browser
return;
// It must be a RuntimeException
// or an IOException that wasn't logged yet
errorHandler.error("[REQUEST_HANDLING_ERROR]", t);
error = t;
}
if (document != null)
try {
// Set the content type
response.setContentType("text/html");
// Send the document's content to the user's browser
document.send(response.getWriter());
} catch (Exception t) {
if (!logSocketException
&& t instanceof java.net.SocketException)
// Some network error occurred or, more likely,
// the user clicked the 'Stop' button of the browser
return;
// It must be a RuntimeException
// or an IOException that wasn't logged yet
errorHandler.error("[DOCUMENT_SENDING_ERROR]", t);
// Don't send this error since the sending of the document
// already started
// error = t;
}
}
if (error != null)
try {
// Get the error message
String errorMessage = error.getMessage();
if (errorMessage == null)
errorMessage = error.toString();
// Send the error message
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
errorMessage);
} catch (Exception t) {
// It wasn't possible to send the error message
errorHandler.error("[ERROR_SENDING_ERROR]", t);
}
}
/**
* Performs the HTTP GET operation
*/
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGetOrPost(request, response, false);
}
/**
* Performs the HTTP POST operation
*/
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGetOrPost(request, response, true);
}
//* RESOURCE UTILS
/**
* Gets a bean resource
*/
public String getBeanResourceAsString(String key, boolean optional)
throws ServletException {
try {
return beanRes.getString(key);
} catch (MissingResourceException e) {
if (optional)
return "";
errorHandler.fatalError("[MISSING_BEAN_RESOURCE]", e, key,
beanName);
} catch (ClassCastException e) {
errorHandler.fatalError("[BEAN_RESOURCE_NOT_STRING]", e, key,
beanName);
} catch (Exception t) {
errorHandler.fatalError("[COULDNT_GET_BEAN_RESOURCE]", t, key,
beanName);
}
return null;
}
/**
* Gets a bean resource
*/
public String[] getBeanResourceAsStringArray(String key, boolean optional)
throws ServletException {
try {
return beanRes.getStringArray(key);
} catch (MissingResourceException e) {
if (optional)
return new String[0];
errorHandler.fatalError("[MISSING_BEAN_RESOURCE]", e, key,
beanName);
} catch (ClassCastException e) {
errorHandler.fatalError("[BEAN_RESOURCE_NOT_STRING_ARRAY]", e, key,
beanName);
} catch (Exception t) {
errorHandler.fatalError("[COULDNT_GET_BEAN_RESOURCE]", t, key,
beanName);
}
return null;
}
/**
* Gets a bean resource
*/
public Object getBeanResourceAsStringOrStringArray(String key,
boolean optional) throws ServletException {
try {
Object obj = beanRes.getObject(key);
if (obj instanceof String)
return (String) obj;
else if (obj instanceof String[])
return (String[]) obj;
else
errorHandler.fatalError(
"[BEAN_RESOURCE_NOT_STRING_OR_STRING_ARRAY]", null, key,
beanName);
} catch (MissingResourceException e) {
if (optional)
return "";
errorHandler.fatalError("[MISSING_BEAN_RESOURCE]", e, key,
beanName);
} catch (Exception t) {
errorHandler.fatalError("[COULDNT_GET_BEAN_RESOURCE]", t, key,
beanName);
}
return null;
}
//* SHUTDOWN
/**
* Closes the logger
*/
public void destroy() {
logger.close();
super.destroy();
}
}
ServletResources.properties:
[MISSING_INIT_PARAM]=Missing init parameter\: {0}
[INVALID_INIT_PARAM]=Invalid init parameter\: {0}={1}
[COULDNT_LOAD_BEAN_CLASS]=Couldn''t load the bean class\: {0}
[COULDNT_LOAD_BEAN_RESOURCES]=Couldn''t load the bean resources\: {0}Resources
[COULDNT_INSTANTIATE_BEAN_CLASS]=Couldn''t instantiate the bean class\: {0}
[HTML_FORM_NOT_FOUND]=HTML form not found\: {0} \
\nVerify the {1} initialization parameter and the {2} bean resource
[REQUEST_HANDLING_ERROR]=The handling of the HTTP request failed
[DOCUMENT_SENDING_ERROR]=The sending of the form document failed
[ERROR_SENDING_ERROR]=The sending of the error message failed
[MISSING_BEAN_RESOURCE]=Missing bean resource. Add {0} to {1}Resources
[COULDNT_GET_BEAN_RESOURCE]=Couldn''t get the {0} resource of {1}Resources
[BEAN_RESOURCE_NOT_STRING]=Bean resource not a String. \
Verify {0} of {1}Resources
[BEAN_RESOURCE_NOT_STRING_ARRAY]=Bean resource not a String[]. \
Verify {0} of {1}Resources
[BEAN_RESOURCE_NOT_STRING_OR_STRING_ARRAY]=Bean resource not a String \
or String[]. Verify {0} of {1}Resources
HandlingContext.java:
package com.devsphere.helpers.mapping;
import com.devsphere.mapping.FormData;
import com.devsphere.mapping.ServletFormData;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
/**
* Bean that wraps the request and response objects.
*/
public class HandlingContext implements java.io.Serializable {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected boolean postMethod;
protected boolean allowMultipart;
protected FormData formData;
/**
* Creates the service bean
*/
public HandlingContext(HttpServletRequest request,
HttpServletResponse response) {
this(request, response, request.getMethod().toUpperCase().equals("POST"));
}
/**
* Creates the service bean
*/
public HandlingContext(HttpServletRequest request,
HttpServletResponse response, boolean postMethod) {
this(request, response, postMethod, false);
}
/**
* Creates the service bean
*/
public HandlingContext(HttpServletRequest request,
HttpServletResponse response,
boolean postMethod, boolean allowMultipart) {
this.request = request;
this.response = response;
this.postMethod = postMethod;
this.allowMultipart = allowMultipart;
formData = null;
}
/**
* Returns the request object
*/
public HttpServletRequest getRequest() {
return request;
}
/**
* Returns the response object
*/
public HttpServletResponse getResponse() {
return response;
}
/**
* Returns true if the HTTP method is POST
*/
public boolean isPostMethod() {
return postMethod;
}
/**
* Returns the form data
*/
public FormData getFormData() throws IOException {
if (formData == null)
synchronized (this) {
if (formData == null)
formData = new ServletFormData(request, allowMultipart);
}
return formData;
}
/**
* Returns true if the encoding type is "multipart/form-data"
* and it is allowed.
*/
public boolean isMultipartFormData() throws IOException {
if (getFormData() instanceof ServletFormData)
return ((ServletFormData) getFormData()).isMultipartFormData();
return false;
}
}
The BeanDispatcher servlet acts like an intermediary between GenericHandler and JSP processors. It extends GenericHandler, overrides initialize() and implements process() so that valid beans are forwarded to their JSP processor.
This servlet eliminates the need to have a separate JSP handler for each bean-form-processor group. You just have to add a few lines to 'servlets.properties' (or to 'web.xml' or to its equivalent):
FirstHndl.code=com.devsphere.helpers.mapping.BeanDispatcher
FirstHndl.initparams=\
BEAN_NAME=first.FirstBean,\
BEAN_ID=firstBean,\
BASE_PATH=/firstPath
SecondHndl.code=com.devsphere.helpers.mapping.BeanDispatcher
SecondHndl.initparams=\
BEAN_NAME=second.SecondBean,\
BEAN_ID=secondBean,\
BASE_PATH=/secondPath
...
For each servlet declaration that uses BeanDispatcher, the bean class must be in CLASSPATH, the BEAN_ID must be the id used by the JSP processor and the HTML form together with the JSP processor must be in the BASE_PATH virtual folder.
BeanDispatcher.java:
package com.devsphere.helpers.mapping;
import com.devsphere.logging.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
/**
* Handler that implements the process() method of GenericHandler
* so that valid beans are forwarded to JSP processors.
*
* Init parameters:
* BEAN_NAME - the name of the bean class
* BASE_PATH - the virtual folder of the HTML form and the JSP processor
* ALLOW_MULTIPART - if true, the use of "multipart/form-data" is allowed
* LOG_SOCKET_EXCEPTION - if true, any java.net.SocketException is logged
* BEAN_ID - the bean identifier used by the JSP processor
*
* Needs Servlets API 2.1+
*/
public class BeanDispatcher extends GenericHandler {
public static final String BEAN_ID_PARAM = "BEAN_ID";
public static final String PROC_NAME_KEY = "[PROC_NAME]";
protected String beanID = null;
protected String procName = null;
/**
* Initializes the servlet
*/
protected void initialize() throws ServletException {
super.initialize();
// Get the bean ID
beanID = getInitParameter(BEAN_ID_PARAM);
if (beanID == null || beanID.length() == 0)
errorHandler.fatalError("[MISSING_INIT_PARAM]", null, BEAN_ID_PARAM);
// Get the processor's name
procName = getBeanResourceAsString(PROC_NAME_KEY, false).trim();
}
/**
* Processes a valid bean objects
*/
protected void process(HandlingContext handlingContext,
Object beanObject) throws ServletException, IOException {
// Store the bean object as a request attribute
handlingContext.getRequest().setAttribute(beanID, beanObject);
// Construct the processor's path
String procPath = basePath + procName;
// Process the valid data bean (Servlets 2.1+)
getServletContext().getRequestDispatcher(procPath).forward(
handlingContext.getRequest(), handlingContext.getResponse());
}
}
The mapping framework handles all possible mapping errors whether they are user or application errors. There are, however, many other kinds of errors that may occur in a real application: runtime exceptions, I/O exceptions, etc. The com.devsphere.logging package offers a general logging mechanism for these errors. The ErrorHandler class defines an extensible handling mechanism for errors that uses the logging capabilities of the framework and adds I18N support.
To create an ErrorHandler instance, you must provide one or more resource bundles and an optional logger object. When you need to log a message or an exception, instead of passing an error message, you provide a key and an optional set of String parameters. The handler will get the String associated to the given key within the resource bundle. Then, it uses the format() method of java.text.MessageFormat to build a message using the String resource and the list of parameters.
There are three types of error handling methods: warning(), error() and fatalError(). The warning() method takes a key and a list of String parameters, builds the message and logs it. The error() method takes an additional Exception object that is logged after the error message. The fatalError() is similar to error(), but it throws the Exception object that it gets as parameter. There is a fourth method trace() that may be used for debugging.
The real power of ErrorHandler is that it can be extended to define error handling mechanisms specific to many kinds of applications. The use of such a mechanism across a whole application makes the debugging and the maintenance easier.
ErrorHandler.java:
package com.devsphere.helpers.logging;
import com.devsphere.logging.*;
import java.text.*;
import java.util.*;
/**
* Error handler
*/
public class ErrorHandler {
protected ResourceBundle resources[];
protected AbstractLogger logger;
/**
* Creates the error handler
*/
public ErrorHandler(ResourceBundle bundle) {
this(bundle, null);
}
/**
* Creates the error handler
*/
public ErrorHandler(ResourceBundle bundle, AbstractLogger logger) {
if (bundle == null)
throw new NullPointerException();
this.resources = new ResourceBundle[] { bundle };
this.logger = logger;
}
/**
* Gets the list of resource bundles
*/
public synchronized ResourceBundle[] getResources() {
// Return a clone of the resources array
return (ResourceBundle[]) resources.clone();
}
/**
* Adds a resource bundle
*/
public synchronized void addResources(ResourceBundle bundle) {
if (bundle == null)
throw new NullPointerException();
ResourceBundle temp[] = resources;
int length = resources.length;
resources = new ResourceBundle[length+1];
System.arraycopy(temp, 0, resources, 0, length);
resources[length] = bundle;
}
/**
* Gets the logger
*/
public synchronized AbstractLogger getLogger() {
return logger;
}
/**
* Sets the logger
*/
public synchronized void setLogger(AbstractLogger logger) {
this.logger = logger;
}
/**
* Logs a message and the exception
*/
protected void log(String message, Exception exception) {
if (logger != null)
logger.log(message, exception);
else {
if (message != null)
System.err.println(message);
if (exception != null)
exception.printStackTrace();
}
}
//* TRACE
/**
* Logs a trace
*/
public synchronized void trace(String key) {
String message = getErrorMessage(key, null);
log(message, null);
}
//* WARNING
/**
* Logs a warning
*/
public synchronized void warning(String key) {
warning(key, (String[]) null);
}
/**
* Logs a warning
*/
public synchronized void warning(String key, String arg0) {
warning(key, new String[] { arg0 });
}
/**
* Logs a warning
*/
public synchronized void warning(String key, String arg0, String arg1) {
warning(key, new String[] { arg0, arg1 });
}
/**
* Logs a warning
*/
public synchronized void warning(String key,
String arg0, String arg1, String arg2) {
warning(key, new String[] { arg0, arg1, arg2 });
}
/**
* Logs a warning
*/
public synchronized void warning(String key, String args[]) {
String message = getErrorMessage(key, args);
log(message, null);
}
//* ERROR
/**
* Logs an error
*/
public synchronized void error(String key) {
error(key, null, (String[]) null);
}
/**
* Logs an error
*/
public synchronized void error(String key, Exception exception) {
error(key, exception, (String[]) null);
}
/**
* Logs an error
*/
public synchronized void error(String key, Exception exception,
String arg0) {
error(key, exception, new String[] { arg0 });
}
/**
* Logs an error
*/
public synchronized void error(String key, Exception exception,
String arg0, String arg1) {
error(key, exception, new String[] { arg0, arg1 });
}
/**
* Logs an error
*/
public synchronized void error(String key, Exception exception,
String arg0, String arg1, String arg2) {
error(key, exception, new String[] { arg0, arg1, arg2 });
}
/**
* Logs an error
*/
public synchronized void error(String key, Exception exception,
String args[]) {
String message = getErrorMessage(key, args);
log(message, exception);
}
//* FATAL ERROR
/**
* Logs an error and throws the exception
*/
public synchronized void fatalError(String key)
throws Exception {
fatalError(key, null, (String[]) null);
}
/**
* Logs an error and throws the exception
*/
public synchronized void fatalError(String key, Exception exception)
throws Exception {
fatalError(key, exception, (String[]) null);
}
/**
* Logs an error and throws the exception
*/
public synchronized void fatalError(String key, Exception exception,
String arg0) throws Exception {
fatalError(key, exception, new String[] { arg0 });
}
/**
* Logs an error and throws the exception
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1) throws Exception {
fatalError(key, exception, new String[] { arg0, arg1 });
}
/**
* Logs an error and throws the exception
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1, String arg2) throws Exception {
fatalError(key, exception, new String[] { arg0, arg1, arg2 });
}
/**
* Logs an error and throws the exception
*/
public synchronized void fatalError(String key, Exception exception,
String args[]) throws Exception {
String message = getErrorMessage(key, args);
log(message, exception);
if (exception != null)
throw exception;
else
throw new Exception(message);
}
//* RESOURCE UTILS
/**
* Gets an error message
*/
protected String getErrorMessage(String key, String args[]) {
if (key == null)
return "";
String message = null;
for (int i = 0; i < resources.length; i++)
try {
message = resources[i].getObject(key).toString();
if (args == null)
args = new String[0];
message = MessageFormat.format(message, args);
} catch (MissingResourceException e) {
}
if (message == null) {
StringBuffer buf = new StringBuffer();
buf.append("MissingResource: ").append(key);
if (args != null && args.length > 0) {
buf.append(" (");
for (int i = 0; i < args.length; i++)
buf.append(args[i]).append(", ");
buf.setLength(buf.length()-2);
buf.append(")");
}
message = buf.toString();
}
return message;
}
}
The ServletErrorHandler class extends ErrorHandler and overrides the fatalError() methods so that they throw a ServletException instead of the Exception object. You could use error() to log exceptions from which the servlet can recover and you could call fatalError() to stop the handling of an HTTP request.
ServletErrorHandler.java:
package com.devsphere.helpers.logging;
import com.devsphere.logging.*;
import javax.servlet.*;
import java.util.*;
/**
* Error handler for servlets
*/
public class ServletErrorHandler extends ErrorHandler {
/**
* Creates the error handler
*/
public ServletErrorHandler(ResourceBundle resources) {
super(resources);
}
/**
* Creates the error handler
*/
public ServletErrorHandler(ResourceBundle resources, AbstractLogger logger) {
super(resources, logger);
}
//* FATAL ERROR
/**
* Logs an error and throws a ServletException
*/
public synchronized void fatalError(String key)
throws ServletException {
fatalError(key, null, (String[]) null);
}
/**
* Logs an error and throws a ServletException
*/
public synchronized void fatalError(String key, Exception exception)
throws ServletException {
fatalError(key, exception, (String[]) null);
}
/**
* Logs an error and throws a ServletException
*/
public synchronized void fatalError(String key, Exception exception,
String arg0) throws ServletException {
fatalError(key, exception, new String[] { arg0 });
}
/**
* Logs an error and throws a ServletException
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1) throws ServletException {
fatalError(key, exception, new String[] { arg0, arg1 });
}
/**
* Logs an error and throws a ServletException
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1, String arg2) throws ServletException {
fatalError(key, exception, new String[] { arg0, arg1, arg2 });
}
/**
* Logs an error and throws a ServletException
*/
public synchronized void fatalError(String key, Exception exception,
String args[]) throws ServletException {
String message = getErrorMessage(key, args);
log(message, exception);
if (exception instanceof ServletException)
throw (ServletException) exception;
else
throw new ServletException(message);
}
}
The IOErrorHandler class extends ErrorHandler and overrides the fatalError() methods so that they throw a IOException instead of the Exception object.
IOErrorHandler.java:
package com.devsphere.helpers.logging;
import com.devsphere.logging.*;
import java.io.*;
import java.util.*;
/**
* Error handler for I/O processes
*/
public class IOErrorHandler extends ErrorHandler {
/**
* Creates the error handler
*/
public IOErrorHandler(ResourceBundle resources) {
super(resources);
}
/**
* Creates the error handler
*/
public IOErrorHandler(ResourceBundle resources, AbstractLogger logger) {
super(resources, logger);
}
//* FATAL ERROR
/**
* Logs an error and throws a IOException
*/
public synchronized void fatalError(String key)
throws IOException {
fatalError(key, null, (String[]) null);
}
/**
* Logs an error and throws a IOException
*/
public synchronized void fatalError(String key, Exception exception)
throws IOException {
fatalError(key, exception, (String[]) null);
}
/**
* Logs an error and throws a IOException
*/
public synchronized void fatalError(String key, Exception exception,
String arg0) throws IOException {
fatalError(key, exception, new String[] { arg0 });
}
/**
* Logs an error and throws a IOException
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1) throws IOException {
fatalError(key, exception, new String[] { arg0, arg1 });
}
/**
* Logs an error and throws a IOException
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1, String arg2) throws IOException {
fatalError(key, exception, new String[] { arg0, arg1, arg2 });
}
/**
* Logs an error and throws a IOException
*/
public synchronized void fatalError(String key, Exception exception,
String args[]) throws IOException {
String message = getErrorMessage(key, args);
log(message, exception);
if (exception instanceof IOException)
throw (IOException) exception;
else
throw new IOException(message);
}
}
The ApplicationErrorHandler class extends ErrorHandler and overrides the fatalError() methods so that they call shutdown() instead of throwing the exception. The protected shutdown() method calls System.exit().
ApplicationErrorHandler.java:
package com.devsphere.helpers.logging;
import com.devsphere.logging.*;
import java.util.*;
/**
* Error handler for standalone application
*/
public class ApplicationErrorHandler extends ErrorHandler {
/**
* Creates the error handler
*/
public ApplicationErrorHandler(ResourceBundle resources) {
super(resources);
}
/**
* Creates the error handler
*/
public ApplicationErrorHandler(ResourceBundle resources, AbstractLogger logger) {
super(resources, logger);
}
//* FATAL ERROR
/**
* Logs an error and calls shutdown()
*/
public synchronized void fatalError(String key) {
fatalError(key, null, (String[]) null);
}
/**
* Logs an error and calls shutdown()
*/
public synchronized void fatalError(String key, Exception exception) {
fatalError(key, exception, (String[]) null);
}
/**
* Logs an error and calls shutdown()
*/
public synchronized void fatalError(String key, Exception exception,
String arg0) {
fatalError(key, exception, new String[] { arg0 });
}
/**
* Logs an error and calls shutdown()
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1) {
fatalError(key, exception, new String[] { arg0, arg1 });
}
/**
* Logs an error and calls shutdown()
*/
public synchronized void fatalError(String key, Exception exception,
String arg0, String arg1, String arg2) {
fatalError(key, exception, new String[] { arg0, arg1, arg2 });
}
/**
* Logs an error and calls shutdown()
*/
public synchronized void fatalError(String key, Exception exception,
String args[]) {
String message = getErrorMessage(key, args);
log(message, exception);
shutdown();
}
/**
* Stops the application
*/
protected void shutdown() {
System.exit(1);
}
}
The GUILogger class extends the com.devsphere.logging.AbstractLogger class. The log() methods call the protected append() method that must be defined by subclasses. This logger is extended by AWTLogger and SwingLogger.
GUILogger.java:
package com.devsphere.helpers.logging;
import com.devsphere.logging.*;
import java.io.*;
/**
* Extended by logging mechanisms that use a text area.
*/
public abstract class GUILogger extends AbstractLogger {
/**
* Appends a message to the content of the text area
*/
protected abstract void append(String message);
/**
* Logs a message.
*/
public void log(String message) {
log(message, null);
}
/**
* Logs an exception.
*/
public void log(Throwable throwable) {
log(null, throwable);
}
/**
* Logs a message and an exception.
*/
public synchronized void log(String message, Throwable throwable) {
if (message != null)
append(message);
if (throwable != null) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
throwable.printStackTrace(printWriter);
printWriter.close();
append(stringWriter.toString());
}
}
/**
* This method does nothing.
*/
public void flush() {
}
/**
* This method does nothing.
*/
public void close() {
}
}
The AWTLogger class extends GUILogger and defines the append() method, which calls the method with the same name of an AWT TextArea component.
AWTLogger.java:
package com.devsphere.helpers.logging;
import java.awt.*;
/**
* Logging mechanisms that uses an AWT text area.
*/
public class AWTLogger extends GUILogger {
private TextArea textArea;
/**
* Creates the AWT logger.
*/
public AWTLogger(TextArea textArea) {
if (textArea == null)
throw new NullPointerException();
this.textArea = textArea;
}
/**
* Appends a message to the content of the text area
*/
protected void append(String message) {
textArea.append(message);
textArea.append("\r\n");
}
}
The SwingLogger class extends GUILogger and defines the append() method, which calls the method with the same name of a Swing JTextArea component.
SwingLogger.java:
package com.devsphere.helpers.logging;
import javax.swing.*;
/**
* Logging mechanisms that uses a Swing text area.
*/
public class SwingLogger extends GUILogger {
private JTextArea textArea;
/**
* Creates the Swing logger.
*/
public SwingLogger(JTextArea textArea) {
if (textArea == null)
throw new NullPointerException();
this.textArea = textArea;
}
/**
* Appends a message to the content of the text area
*/
protected void append(String message) {
textArea.append(message);
textArea.append("\r\n");
}
}
One of the examples uses the BeanDispatcher servlet.
The GenericHandler servlet is extended by FormMailer, which sends the form data to one or more addresses and returns a static HTML page to the user's browser.
MailMonitor and MonitorStarter use the ApplicationErrorHandler.
|