From d654cd9bd5efc6a17fdf6c0befe5b0618e916415 Mon Sep 17 00:00:00 2001 From: mdb Date: Tue, 13 Feb 2001 20:00:28 +0000 Subject: [PATCH] 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 --- .../com/samskivert/webmacro/ExceptionMap.java | 142 ++++++++++++++++++ .../samskivert/webmacro/FriendlyServlet.java | 42 ++++-- .../src/java/com/samskivert/webmacro/Log.java | 14 ++ .../src/java/com/samskivert/webmacro/Makefile | 4 +- 4 files changed, 191 insertions(+), 11 deletions(-) create mode 100644 projects/samskivert/src/java/com/samskivert/webmacro/ExceptionMap.java create mode 100644 projects/samskivert/src/java/com/samskivert/webmacro/Log.java diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/ExceptionMap.java b/projects/samskivert/src/java/com/samskivert/webmacro/ExceptionMap.java new file mode 100644 index 00000000..915eec37 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/webmacro/ExceptionMap.java @@ -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 exceptionmap.properties 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}"; +} diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/FriendlyServlet.java b/projects/samskivert/src/java/com/samskivert/webmacro/FriendlyServlet.java index 8e64804a..0a545f79 100644 --- a/projects/samskivert/src/java/com/samskivert/webmacro/FriendlyServlet.java +++ b/projects/samskivert/src/java/com/samskivert/webmacro/FriendlyServlet.java @@ -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. * *

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 exceptionmap.properties 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: * *

- * 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.
  * 
* * The message associated with the exception will be substituted into the - * error string in place of {m}. + * error string in place of {m}. 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. + * + * Note: 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 + * "error") 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"; } diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/Log.java b/projects/samskivert/src/java/com/samskivert/webmacro/Log.java new file mode 100644 index 00000000..7898992d --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/webmacro/Log.java @@ -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"); +} diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/Makefile b/projects/samskivert/src/java/com/samskivert/webmacro/Makefile index f00e18b1..3087464c 100644 --- a/projects/samskivert/src/java/com/samskivert/webmacro/Makefile +++ b/projects/samskivert/src/java/com/samskivert/webmacro/Makefile @@ -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