2012-03-12 2 views
1

많은 다른 응용 프로그램에 DAO를 노출하기 위해 Spring's 'HTTP Invoker' remoting solution을 사용하고 있지만 단일 서버에 모든 데이터베이스 액세스 권한이 있습니다.Spring Remoting HTTP 호출자 - 예외 처리

이 방법은 잘 작동하지만, 서버가 HibernateSystemException을 던지면 Spring은 그것을 순차 화하여 와이어를 통해 클라이언트로 되돌려 보냅니다. 이것은 클라이언트가 classpath에 HibernateSystemException을 가지지 않았기 때문에 작동하지 않는다.

Spring Remoting은 클라이언트와 서버간에 공통적으로 이와 같은 문제를 피하기 위해 지정한 예외에 내 예외를 포함시킬 수 있습니까?

DAO가 try/catch에서하는 모든 것을 래핑하여 서버 코드에서 수행 할 수 있음을 알고 있습니다.하지만 그다지 의심의 여지가 없습니다.

감사합니다, 로이

답변

3

이 문제도 발생했습니다. 나는 HTTP Invoker를 통해 Spring 3.1, JPA 2 및 Hibernate를 JPA 공급자로 사용하여 데이터베이스에 액세스하는 서비스를 노출하려고합니다.

문제를 해결하려면 사용자 지정 인터셉터와 WrappedException이라는 예외를 작성했습니다. 인터셉터는 서비스에 의해 발생 된 예외를 포착하고 리플렉션 및 설정자를 사용하여 예외 및 원인을 WrappedException으로 변환합니다. 클라이언트가 클래스 경로에 WrappedException을 가지고 있다고 가정하면 스택 트레이스 및 원래 예외 클래스 이름을 클라이언트에서 볼 수 있습니다.

이렇게하면 클래스 경로에 클라이언트가 Spring DAO를 가질 필요가 없어지고 원래 스택 추적 정보가 손실되지 않습니다.

인터셉터

public class ServiceExceptionTranslatorInterceptor implements MethodInterceptor, Serializable { 

    private static final long serialVersionUID = 1L; 

    @Override 
    public Object invoke(MethodInvocation invocation) throws Throwable { 
     try { 
      return invocation.proceed(); 
     } catch (Throwable e) { 
      throw translateException(e); 
     } 
    } 

    static RuntimeException translateException(Throwable e) { 
     WrappedException serviceException = new WrappedException(); 

     try { 
      serviceException.setStackTrace(e.getStackTrace()); 
      serviceException.setMessage(e.getClass().getName() + 
        ": " + e.getMessage()); 
      getField(Throwable.class, "detailMessage").set(serviceException, 
        e.getMessage()); 
      Throwable cause = e.getCause(); 
      if (cause != null) { 
       getField(Throwable.class, "cause").set(serviceException, 
         translateException(cause)); 
      } 
     } catch (IllegalArgumentException e1) { 
      // Should never happen, ServiceException is an instance of Throwable 
     } catch (IllegalAccessException e2) { 
      // Should never happen, we've set the fields to accessible 
     } catch (NoSuchFieldException e3) { 
      // Should never happen, we know 'detailMessage' and 'cause' are 
      // valid fields 
     } 
     return serviceException; 
    } 

    static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException { 
     Field f = clazz.getDeclaredField(fieldName); 
     if (!f.isAccessible()) { 
      f.setAccessible(true); 
     } 
     return f; 
    } 

} 

예외

public class WrappedException extends RuntimeException { 

    private static final long serialVersionUID = 1L; 

    private String message = null; 

    public void setMessage(String message) { 
     this.message = message; 
    } 

    @Override 
    public String toString() { 
     return message; 
    } 
} 

콩 배선

<bean id="exceptionTranslatorInterceptor" class="com.YOURCOMPANY.interceptor.ServiceExceptionTranslatorInterceptor"/> 

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
    <property name="beanNames" value="YOUR_SERVICE" /> 
    <property name="order" value="1" /> 
    <property name="interceptorNames"> 
     <list> 
      <value>exceptionTranslatorInterceptor</value> 
     </list> 
    </property> 
</bean> 
+0

내가보기에 완벽 했어. 이 문제로 잠시 앉아서 인터셉터를 처리 할 필요가 있지만 결코 코딩 할 필요가 없다는 것을 알았습니다. 감사! –

0

난 당신이 자신의 클래스 경로에 HibernateSystemException을 가지고 고객을하지 않으려는 이해할 수 있지만 적절 HTTPInvoker를 사용하는 경우 나, 그들이해야 주장 할 것입니다. 서비스 외관/인터페이스 레이어가되도록 설계되지 않았습니다. RMI 대신 HTTP를 사용하여 원격 JVM에서 Java 메소드를 실행할 수 있습니다.

그래서 클라이언트가 Hibernate에 대한 의존성을 가지기를 정말로 원하지 않는다면 try/catch 블록을 사용하는 것이 좋습니다. (나는 디버깅을 고통스럽게 할 것이기 때문에 그것에 반대 할 것이다. 스택 추적은 이제 클라이언트와 서버로 나누어진다.)

본인은 직접 사용하지 않았지만 org.springframework.remoting.support.RemoteExporter.setInterceptors(Object[]) 메서드를 사용하여 try/catch를 모든 장소에 추가하는 대신 한 곳에서 특정 예외를 잡을 수있는 방법을 추가 할 수있었습니다.

0

DAO 앞에있는 Facade 레이어의 try/catch는 정확히입니다. 반환 할 예외를 완전히 제어하려면 논쟁의 여지가 있습니다. 나는 그것이 초기에 추악하다고 느끼는 것에 동의하지만, 그것은 클라이언트와 DAO 사이의 중요한 계층이다.

무효 반환 유형을 사용하지 않고 일종의 OperationStatus 객체를 반환하여 상점 데이터 API 호출에 대한 결과 (성공 여부)와 오류 메시지를 전달할 수도 있습니다.

0

가 나는 해결책의 사용 N1H4L과 유사하지만 AspectJ.

먼저 클라이언트가 클래스를 확장하기 위해 알아야 할 모든 예외를 만들었습니다 BusinessException (제 경우에는 서비스 인터페이스와 DTO가있는 jar 파일의 RuntimeException이라는 매우 간단한 하위 클래스입니다).

저는 클라이언트가 서비스의 내부에 대해 많이 알기를 원하지 않기 때문에 "내부 서버 오류"라고 말합니다. 여기

package com.myproduct.myservicepackage; 

import com.myproduct.BusinessException; 
import org.aspectj.lang.*; 
import org.aspectj.lang.annotation.*; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public class InternalServerErrorExceptionAspect { 
    @Pointcut("execution(public * com.myproduct.myservicepackage..*Service.*(..))") 
    public void publicServiceMethod() {} 

    @Around("publicServiceMethod()") 
    public Object hideNonBusinessExceptions(ProceedingJoinPoint jp) throws Throwable { 
     try { 
      return jp.proceed(); 
     } catch (BusinessException e) { 
      throw e; 
     } catch (RuntimeException e) { 
      e.printStackTrace(); 
      throw new RuntimeException("Internal server error.") 
     } 
    } 
} 

는 BusinessException 클래스의 :

package com.myproduct.BusinessException; 

public class BusinessException extends RuntimeException { 

    private static final long serialVersionUID = 8644864737766737258L; 

    public BusinessException(String msg) { 
     super(msg); 
    } 

} 
0

내가 AspectJ를 예외를 포장 사용했지만 그것은 봄 프록시에서 발생하는 예외, 예를 들어, 작동하지 않습니다 어노테이션 @ 데이터베이스로의 연결이 실패 할 때 트랜잭션. 그러나 RmiServiceExporter의 setInterceptor 메서드는 완벽하게 작동합니다.