com.samskivert.servlet.util.ExceptionMap Maven / Gradle / Ivy
Show all versions of samskivert Show documentation
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2011 Michael Bayne, et al.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.servlet.util;
import java.io.*;
import java.util.*;
import com.samskivert.util.ConfigUtil;
import static com.samskivert.Log.log;
/**
* The exception map is used to map exceptions to error messages based on
* a static, server-wide configuration.
*
* The configuration file is 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:
*
*
* # Exception mappings (lines beginning with # are ignored)
* com.samskivert.servlet.util.FriendlyException: An error occurred while \
* processing your request: {m}
*
* # 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 \
* your request. Please try again later.
*
*
* The message associated with the exception will be substituted into the
* error string in place of {m}
. The exceptions should be
* listed in order of most to least specific, as the first mapping for
* which the exception to report is an instance of the listed exception
* will be used.
*
* Note: These exception mappings will generally be used for
* all requests (perhaps some day only for requests associated with a
* particular application). Regardless, this error handling mechanism
* should not be used for request specific errors. For example, an SQL
* exception reporting a duplicate key should probably be caught and
* reported specifically by the appropriate populator (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 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
ClassLoader cld = ExceptionMap.class.getClassLoader();
InputStream config = ConfigUtil.getStream(PROPS_NAME, cld);
if (config == null) {
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
final ArrayList classes = new ArrayList();
Properties loader = new Properties() {
@Override public Object put (Object key, Object value) {
classes.add((String)key);
_values.add((String)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 < classes.size(); i++) {
String exclass = classes.get(i);
try {
Class> cl = Class.forName(exclass);
// replace the string with the class object
_keys.add(cl);
} catch (Throwable t) {
log.warning("Unable to resolve exception class.", "class", exclass,
"error", t);
_values.remove(i);
i--; // back on up a notch
}
}
} catch (IOException ioe) {
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 = _keys.get(i);
if (cl.isInstance(ex)) {
msg = _values.get(i);
break;
}
}
return msg.replace(MESSAGE_MARKER, ex.getMessage());
}
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}";
}