Added the code that actually maps exceptions to error messages.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@45 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-02-13 20:00:28 +00:00
parent e00c6cc066
commit d654cd9bd5
4 changed files with 191 additions and 11 deletions
@@ -0,0 +1,142 @@
//
// $Id: ExceptionMap.java,v 1.1 2001/02/13 20:00:28 mdb Exp $
package com.samskivert.webmacro;
import java.io.*;
import java.util.*;
import com.samskivert.util.StringUtil;
/**
* The exception map is used to load the exception to error message
* mapping information and to look up the appropriate response for a given
* exception instance.
*
* @see FriendlyServlet
*/
public class ExceptionMap
{
/**
* Searches for the <code>exceptionmap.properties</code> file in the
* classpath and loads it. If the file could not be found, an error is
* reported and a default set of mappings is used.
*/
public static synchronized void init ()
{
// only initialize ourselves once
if (_keys != null) {
return;
} else {
_keys = new ArrayList();
_values = new ArrayList();
}
// first try loading the properties file without a leading slash
InputStream config = getStream(PROPS_NAME);
if (config == null) {
// some JVMs require the leading slash, some don't
config = getStream("/" + PROPS_NAME);
}
// still no props, then we complain
if (config == null) {
Log.log.warning("Unable to load " + PROPS_NAME +
" from CLASSPATH.");
} else {
// otherwise process ye old config file.
try {
// we'll do some serious jiggery pokery to leverage the
// parsing implementation provided by
// java.util.Properties. god bless method overloading
Properties loader = new Properties() {
public Object put (Object key, Object value)
{
_keys.add(key);
_values.add(value);
return key;
}
};
loader.load(config);
// now cruise through and resolve the exceptions named as
// keys and throw out any that don't appear to exist
for (int i = 0; i < _keys.size(); i++) {
String exclass = (String)_keys.get(i);
try {
Class cl = Class.forName(exclass);
// replace the string with the class object
_keys.set(i, cl);
} catch (Throwable t) {
Log.log.warning("Unable to resolve exception " +
"class. [class=" + exclass +
", error=" + t + "].");
_keys.remove(i);
_values.remove(i);
i--; // back on up a notch
}
}
} catch (IOException ioe) {
Log.log.warning("Error reading exception mapping file: " +
ioe);
}
}
}
/**
* Looks up the supplied exception in the map and returns the most
* specific error message available for exceptions of that class.
*
* @param ex The exception to resolve into an error message.
*
* @return The error message to which this exception maps (properly
* populated with the message associated with this exception
* instance).
*/
public static String getMessage (Throwable ex)
{
String msg = DEFAULT_ERROR_MSG;
for (int i = 0; i < _keys.size(); i++) {
Class cl = (Class)_keys.get(i);
if (cl.isInstance(ex)) {
msg = (String)_values.get(i);
break;
}
}
return StringUtil.replace(msg, MESSAGE_MARKER, ex.getMessage());
}
protected static InputStream getStream (String path)
{
// first try using the classloader that loaded us
Class c = ExceptionMap.class;
InputStream in = c.getResourceAsStream(path);
if (null == in) {
// if that didn't work, try the system classloader
c = Class.class;
in = c.getResourceAsStream(path);
}
return in;
}
public static void main (String[] args)
{
ExceptionMap map = new ExceptionMap();
System.out.println(map.getMessage(new Exception("Test error")));
}
protected static List _keys;
protected static List _values;
// initialize ourselves
static { init(); }
protected static final String PROPS_NAME = "exceptionmap.properties";
protected static final String DEFAULT_ERROR_MSG = "Error: {m}.";
protected static final String MESSAGE_MARKER = "{m}";
}
@@ -1,5 +1,5 @@
//
// $Id: FriendlyServlet.java,v 1.1 2001/02/13 18:49:41 mdb Exp $
// $Id: FriendlyServlet.java,v 1.2 2001/02/13 20:00:28 mdb Exp $
package com.samskivert.webmacro;
@@ -25,23 +25,41 @@ import org.webmacro.servlet.*;
* webmacro procedure.
*
* <p> The process of mapping exceptions to friendly error messages is
* done through a properties file loaded via the classpath. The properties
* file should be named 'exceptionmap.properties' and placed in a
* directory contained in the classpath of the JVM in which the servlet is
* executed. The file should contain mappings from exception classes to
* friendly error messages. For example:
* done through a configuration file loaded via the classpath. The file
* should be named <code>exceptionmap.properties</code> and placed in the
* classpath of the JVM in which the servlet is executed. The file should
* contain colon-separated mappings from exception classes to friendly
* error messages. For example:
*
* <pre>
* com.samskivert.webmacro.FriendlyException = An error occurred while \
* # Exception mappings (lines beginning with # are ignored)
* com.samskivert.webmacro.FriendlyException: An error occurred while \
* processing your request: {m}
* java.sql.SQLException = The database is currently unavailable. Please \
*
* # lines ending with \ are continued on the next line
* java.sql.SQLException: The database is currently unavailable. Please \
* try your request again later.
* java.lang.Exception = An unexpected error occurred while processing \
*
* java.lang.Exception: An unexpected error occurred while processing \
* your request. Please try again later.
* </pre>
*
* The message associated with the exception will be substituted into the
* error string in place of <code>{m}</code>.
* error string in place of <code>{m}</code>. The exceptions should be
* listed in order of most specific to least specific, for the first
* mapping for which the exception to report is an instance of the
* exception in the left hand side will be used.
*
* <em>Note:</em> These exception mappings are used for all servlets
* (perhaps some day only for servlets associated with a particular
* application identifier). Regardless, this error handling mechanism
* should not be used for servlet specific errors. For example, an SQL
* exception reporting a duplicate key should probably be caught and
* reported specifically by the servlet (it can still leverage the pattern
* of inserting the error message into the context as
* <code>"error"</code>) rather than relying on the default SQL exception
* error message which is not likely to be meaningful for such a
* situation.
*/
public abstract class FriendlyServlet extends WMServlet
{
@@ -59,6 +77,7 @@ public abstract class FriendlyServlet extends WMServlet
try {
populateContext(ctx, tmpl);
} catch (Exception e) {
ctx.put(ERROR_KEY, ExceptionMap.getMessage(e));
}
return tmpl;
@@ -87,4 +106,7 @@ public abstract class FriendlyServlet extends WMServlet
*/
protected abstract void populateContext (WebContext ctx, Template tmpl)
throws Exception;
/** This is the key used in the context for error messages. */
protected static final String ERROR_KEY = "error";
}
@@ -0,0 +1,14 @@
//
// $Id: Log.java,v 1.1 2001/02/13 20:00:28 mdb Exp $
package com.samskivert.webmacro;
/**
* A placeholder class that contains a reference to the log object used by
* the webmacro package.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("com.samskivert.webmacro");
}
@@ -1,10 +1,12 @@
#
# $Id: Makefile,v 1.1 2001/02/13 18:49:41 mdb Exp $
# $Id: Makefile,v 1.2 2001/02/13 20:00:28 mdb Exp $
ROOT = ../../..
SRCS = \
ExceptionMap.java \
FriendlyException.java \
FriendlyServlet.java \
Log.java \
include $(ROOT)/build/Makefile.java