Permalink
Browse files

Map exceptions to translatable message codes with the help of a child…

… class of Spring's SimpleMappingExceptionResolver
  • Loading branch information...
aheusingfeld committed Oct 19, 2011
1 parent db38644 commit eb9fb0d8b46c50c27a8fdb1e2c01a498fed4b136
@@ -0,0 +1,76 @@
/**
*
*/
package de.goldstift.photoo.web.exceptions;
/**
* TODO describe type AnalyzedApplicationException
* @author ahe
* @since 26.01.2011 23:17:04
*/
public class AnalyzedApplicationException extends Exception
{
/**
* Version of this class file.
*/
private static final long serialVersionUID = 1L;
private Throwable rootCause;
private String msgCode;
public AnalyzedApplicationException()
{
super();
}
public AnalyzedApplicationException(String arg0, Throwable arg1)
{
super(arg0, arg1);
}
public AnalyzedApplicationException(String arg0)
{
super(arg0);
}
public AnalyzedApplicationException(Throwable arg0)
{
super(arg0);
}
/**
* @return the originalException
*/
public final Throwable getOriginalException()
{
return getCause();
}
/**
* @return the rootCause
*/
public final Throwable getRootCause()
{
return rootCause;
}
/**
* @param rootCause the rootCause to set
*/
public final void setRootCause(Throwable rootCause)
{
this.rootCause = rootCause;
}
/**
* @return the msgCode
*/
public final String getMsgCode()
{
return msgCode;
}
/**
* @param msgCode the msgCode to set
*/
public final void setMsgCode(String msgCode)
{
this.msgCode = msgCode;
}
}
@@ -0,0 +1,142 @@
/**
*
*/
package de.goldstift.photoo.web.exceptions;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
/**
* This class uses the specified {@link #messageMapping} to resolve a well-formed, defined message
* for the caught exception from "application_XX.properties" files. The #exceptionMappings are then
* used to resolve a specific view name for the caught exception.
* The default behaviour of DispatcherServlet is to propagate all exceptions to the servlet
* container: this is used as fallback behaviour here.<br>
*
* <strong>Usage example:</strong>
* <pre>
&lt;bean
class="de.goldstift.photoo.web.exceptions.I18nMappingExceptionResolver"
p:defaultErrorView="uncaughtException" p:defaultMessage="exceptions.unexpected"&gt;
&lt;!-- NOTE: 'exceptionMappings' are used to resolve view names in the super class --&gt;
&lt;property name="exceptionMappings"&gt;
&lt;props&gt;
&lt;prop key=".AnalyzedApplicationException"&gt;uncaughtException&lt;/prop&gt;
&lt;prop key=".NoSuchRequestHandlingMethodException"&gt;resourceNotFound&lt;/prop&gt;
&lt;prop key=".TypeMismatchException"&gt;resourceNotFound&lt;/prop&gt;
&lt;prop key=".MissingServletRequestParameterException"&gt;resourceNotFound&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;property name="messageMapping"&gt;
&lt;map&gt;
&lt;!-- NOTE: the given 'entry values' have to be defined in the several "application.properties" files! --&gt;
&lt;entry value="exceptions.db.duplicateentry"&gt;
&lt;key&gt;
&lt;bean class="de.goldstift.photoo.web.exceptions.MessageMatchCriteria" p:classNamePart="ConstraintViolationException" p:messagePart="Duplicate entry"/&gt;
&lt;/key&gt;
&lt;/entry&gt;
&lt;entry value="exceptions.db.constraint"&gt;
&lt;key&gt;
&lt;bean class="de.goldstift.photoo.web.exceptions.MessageMatchCriteria" p:classNamePart="ConstraintViolationException"/&gt;
&lt;/key&gt;
&lt;/entry&gt;
&lt;/map&gt;
&lt;/property&gt;
&lt;/bean&gt;
* </pre>
*
* @author ahe
* @since 26.01.2011 23:11:56
*/
public class I18nMappingExceptionResolver extends SimpleMappingExceptionResolver
{
private static final Logger LOG = LoggerFactory.getLogger(I18nMappingExceptionResolver.class);
private Map<MessageMatchCriteria, String> messageMapping;
private String defaultMessage;
/**
* Overrides method of the super class to resolve a specific view name for the caught Exception.
* If no specific
*
* {@inheritDoc}
*
* @see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver#getModelAndView(java
* .lang.String, java.lang.Exception, javax.servlet.http.HttpServletRequest)
*/
@Override
protected ModelAndView getModelAndView(final String viewName, final Exception ex, final HttpServletRequest request)
{
Throwable rootCause = getRootCause(ex);
final String message;
if (rootCause instanceof Exception)
{
message = getMessageByClassName((Exception) rootCause, rootCause.getMessage());
} else {
message = getMessageByClassName(ex, ex.getMessage());
}
final AnalyzedApplicationException analyzedEx = new AnalyzedApplicationException(message,
ex);
analyzedEx.setRootCause(rootCause);
return super.getModelAndView(viewName, analyzedEx, request);
}
private String getMessageByClassName(final Exception ex, final String message)
{
for (MessageMatchCriteria key : messageMapping.keySet())
{
if (getDepth(key.getClassNamePart(), ex) > -1
// either there is no MessagePart or it must be contained in the message!
&& (!StringUtils.hasText(key.getMessagePart()) || message.contains(key
.getMessagePart())))
{
return messageMapping.get(key);
}
}
LOG.warn("No message mapping for the criteria found! message: '" + message + "' - Exception: ", ex);
return defaultMessage;
}
/**
* Uses redundant calls to retrieve an exception's root cause.
*
* @param exception - the exception whose cause shall be retrieved
* @return the root cause
*/
private Throwable getRootCause(final Throwable exception)
{
Assert.notNull(exception, "Specified exception must not be null");
if (exception.getCause() != null)
{
return getRootCause(exception.getCause());
}
return exception;
}
/**
* @param messageMapping the messageMapping to set
*/
public final void setMessageMapping(final Map<MessageMatchCriteria, String> messageMapping)
{
this.messageMapping = messageMapping;
}
/**
* @param defaultMessage the defaultMessage to set
*/
public final void setDefaultMessage(final String defaultMessage)
{
this.defaultMessage = defaultMessage;
}
}
@@ -0,0 +1,50 @@
/**
*
*/
package de.goldstift.photoo.web.exceptions;
/**
* A MessageMatchCriteria describes what is to check to determine whether a message key shall be
* used to translate a message.
*
* @author ahe
* @since 26.01.2011 23:53:45
*/
public class MessageMatchCriteria
{
private String classNamePart;
private String messagePart;
/**
* @return the classNamePart
*/
public final String getClassNamePart()
{
return classNamePart;
}
/**
* @param classNamePart the classNamePart to set
*/
public final void setClassNamePart(String classNamePart)
{
this.classNamePart = classNamePart;
}
/**
* @return the messagePart
*/
public final String getMessagePart()
{
return messagePart;
}
/**
* @param messagePart the messagePart to set
*/
public final void setMessagePart(String messagePart)
{
this.messagePart = messagePart;
}
}
@@ -1,6 +1,10 @@
#Updated at Sat Sep 24 22:18:09 CEST 2011
#Sat Sep 24 22:18:09 CEST 2011
#Updated at Wed Oct 19 23:11:18 CEST 2011
#Wed Oct 19 23:11:18 CEST 2011
application_name=Photoo
exceptions.db.constraint=Your request violated a database constraint\! Therefore your data could not be saved.
exceptions.db.duplicateentry=The record could not be saved as there is already one in the database having the same unique identifier\!
exceptions.originalmessage=untranslated original message
exceptions.unexpected=An unmapped exception occurred. If the same exception occurs more than once, please contact your system administrator.
label_de_goldstift_photoo_domain_event=Event
label_de_goldstift_photoo_domain_event_fromdate=From Date
label_de_goldstift_photoo_domain_event_id=Id
@@ -45,6 +49,7 @@ label_de_goldstift_photoo_domain_photo_title=Title
label_de_goldstift_photoo_domain_photo_version=Version
label_de_goldstift_photoo_domain_photofile=Photo File
label_de_goldstift_photoo_domain_photofile_filename=Filename
label_de_goldstift_photoo_domain_photofile_filesize=File Size
label_de_goldstift_photoo_domain_photofile_folder=Folder
label_de_goldstift_photoo_domain_photofile_height=Height
label_de_goldstift_photoo_domain_photofile_id=Id
@@ -63,6 +63,7 @@ selenium_menu_test_suite=Test Suite
#exception
exception_message=Exception Message
exceptions_originalmessage=Original exception message
exception_stacktrace=Exception Stack Trace
exception_cookie=Cookies
exception_details=Details
@@ -95,5 +96,5 @@ security_login_form_name=Name
security_login_form_name_message=Enter your name
security_login_form_password=Password
security_login_form_password_message=Enter your password
security_login_unsuccessful=Your login attempt was not successful, try again. Reason:
security_login_unsuccessful=Your login attempt was not successful, try again. Reason:
security_logout=Logout
Oops, something went wrong.

0 comments on commit eb9fb0d

Please sign in to comment.