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