2007년 8월 30일 목요일

Spring + DWR 을 이용한 Form Submission처리...

DWR 에서 post방식으로 전달된 데이타를 Spring에서 받어서 처리할수 있다.

Spring 은 Form Submission을 받으면 Form handler는 새로운 FormBackingObject를 생성하고 폼에 의하여 입력이 되여진 각 Elements들은 Reflection방식으로 적절한 setter메소드의하여 POJO에 세팅이 되여진다. 이러한 바인딩 과정은 모든 input elements에 발생한다.

DWR을 이용한 AJAX Request는 일반적인 Form Submission방식이 아니고 넘기는것은 오로지 FormID 와 관련 데이타뿐이기 때문에 Spring의 FormBackingObject는 자동으로 이렇게 넘겨받은 값들을 바인딩시켜줄수 없다.

InvocationTargetException 속에는 어떤 것들이 들어있을가?

InvocationTargetException 속에는 단순 Exception만 들어있는것은 아니다!

InvocationTargetException 속에는 Throwable 객체가 내포되여있다.
InvocationTargetException 생성자만 보아도 쉽게 알수 있는 사실이다.

InvocationTargetException(Throwable target)

더욱 놀라운 사실은 null도 Throwable 객체속에 할당되여 들어갈수 있다는것이다.
실제 리턴하는 값은 InvocationTargetException을 야기시키는 예외인데 만약 없으면 getCause()가 null을 리턴하게 되는것이다.

InvocationTargetException.getCause() 를 호출할때 프로그래머는 모두 네가지 종류의 cause를 리턴 받을수 있다.

  1. null
  2. unchecked exceptions (RuntimeExceptions such as IllegalArgumentException, NullPointerException, etc)
  3. checked exceptions (e.g., NameNotFoundException, YourApplicationException)
  4. java.lang.Error (e.g., StackOverflowError, OutOfMemoryError, InternalError, UnknownError)

때문에 InvocationTargetException을 처리할때 위 4가지 예외를 모두 고려하여 처리해야만 한다.
아래에 실제 InvocationTargetException 처리용 샘플코드를 첨부하겠다.

try {
 helloMethod.invoke(foo);
} catch (IllegalAccessException ex) {
 logger.log(Level.SEVERE, "Invocation failed.", ex);
} catch (InvocationTargetException ex) {
 Throwable cause = ex.getCause();
 if(cause == null) {
  throw new IllegalStateException(
   "Got InvocationTargetException, but the cause is null.", ex);
 } else if(cause instanceof RuntimeException) {
  throw (RuntimeException) cause;
 } else if(cause instanceof Exception) {
  logger.log(Level.SEVERE, "Invocation failed with cause: ", cause);
 } else {
  logger.log(Level.SEVERE, "Invocation failed with error: ", cause);
 }

}

자주색으로 표기된부분이 바로 InvocationTargetException이 발생하였을시 처리를 해주는 부분이다.
만약 cause가 null이라면 IllegalStateException을 만들고 그속에 InvocationTargetException을 담아서 던진다.
또한 이것이 아닌 RuntimeException으로 기인한 예외라면 해당 예외를 던진다.

2007년 8월 29일 수요일

Spring MVC의 HandlerExceptionResolver 를 사용한 예외 처리

Spring MVC에서의 예외처리


Spring에서 예외 처리하기 위해서는 모두 두가지 방법이 있다.

간단히 설정파일로만 예외처리를 하려면 SimpleMappingExceptionResolver를 사용하면 되겠다.

Spring MVC에서는 쉽게 예외처리와 설정을 하기 위해서 SimpleMappingExceptionResolver를 제공한다. SimpleMappingExceptionResolver을 사용함으로써 컨트롤러나 인터셉터에 산재해 있는 예외 처리에 관련된 설정들을 중앙 집중(centralize)할 수 있게 된다.

[code java] protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // Log exception, both at debug log level and at warn level, if desired. if (logger.isDebugEnabled()) { logger.debug("Resolving exception from handler [" + handler + "]: " + ex); } logException(ex, request); // Expose ModelAndView for chosen error view. String viewName = determineViewName(ex, request); if (viewName != null) { // Apply HTTP status code for error views, if specified. // Only apply it if we're processing a top-level request. Integer statusCode = determineStatusCode(request, viewName); if (statusCode != null) { applyStatusCodeIfPossible(request, response, statusCode.intValue()); } return getModelAndView(viewName, ex, request); } else { return null; } } [/code] SimpleMappingExceptionResolver클래스에서 가장 중요한 메소드는 doResolveException인데 위에서와 같이 구현이 되여져있다.
매개 Exception마다 지정된 ModelAndView를 할당해 줄수 있고 이런것들은 doResolveException에 의하여 처리되고 매핑된 ModelAndView페이지를 리턴하게 된다.

Spring내에서 커스터마이징된 익셉션을 관리하기 위하여 applicationContext.xml안에 아래와 같이 설정을 해주면 된다.
[code xml] <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Exception Resolver --> <bean id="exceptionMapping" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="DataNotFoundException">exception/ErrorMessage</prop> </props> </property> <property name="exceptionAttribute" value="exceptionMsg" /> <property name="defaultErrorView" value="Error" /> </bean> </beans> [/code] DataNotFoundException이 발생하게 되면 exception/DataNotFoundException ViewName을 리턴하게 되고 SimpleMappingExceptionResolver에서 viewName에 의거하여 해당 ModelAndView를 리턴하게 된다. view를 찾을 때에는 viewResolver가 역시 사용 된다.
하나 이상의 excpetion을 처리하는 resolver가 등록되어 있는 경우에는 Ordered 인터페이스를 구현해 우선 순위를 정할 수 있다.
위 속성을 제외하고도 SimpleMappingExceptionResolver는 여러가지 유용한 속성들을 노출시키고 있다. 아래에 자주쓰이는 몇몇 속성들에 대하여 설명할가 한다.

mappedHandlers : 특정된 맵 핸들러를 세팅해줄수 있다.
mappedHandlerClasses : 특정된 맵 핸들러클래스를 세팅해줄수 있다.
defaultErrorView : 지정된 예외가 아닌 예외일때 기본적으로 포워딩해줄 view페이지를 세팅할수있다.
defaultStatusCode : 예외가 떴을대 HTTP 상태코드를 세팅해줄수가 있다. 단 이 세팅은 Top - Level Request에만 유효하다.
exceptionAttribute : 예외가 노출시킬 기본 모델 속성을 세팅할수 있다. 기본적으로 exception으로 설정이 되여있으나 경우에 따라 변경할수 있다. 만약 이 속성에 값을 세팅하지 않으면 기본적으로 view페이지에서 ${exception.message}를 통하여 예외메세지를 받을수 있다.

아래에 Exception의 상속을 구조를 알아보겠다.
ExceptionDetail 이 만약 ExceptionBase를 상속받은 상태에서 ExceptionDetail 이 발생하였고 또한 설정파일이 아래와 같이 설정이 되여져 있는 상태라면
[code xml] <property name="exceptionMappings"> <props> <prop key="ExceptionBase">basePage</prop> </props> </property> [/code] basePage가 viewName으로 리턴을 받게 된다.

프로그래밍적 즉 하드코딩방식으로 예외처리를 할려면 HandlerExceptionResolver 인터페이스를 직접 상속받어가지고 구현하여야만 한다.
[code java] import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; public class BaseExceptionResolver implements HandlerExceptionResolver { private String view = null; public void setView(String view) { this.view = view; } public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj, Exception exception) { request.setAttribute("exception",exception); return new ModelAndView(view); } } [/code] 또한 bean 은 아래와 같이 설정해주어야 한다.
[code xml] <bean id="exceptionResolver" class="BaseExceptionResolver"> <property name="view" value="error"/> </bean> [/code] 윗방법을 쓰는 원인은 Request로 Exception을 넘기기 위해서이다.