2012-03-12 16 views
1

私はSpring's 'HTTP Invoker' remoting solutionを使用してDAOをさまざまなアプリケーションに公開していますが、すべてのデータベースへのアクセスは1台のサーバーで行います。Spring Remoting HTTPの呼び出し側 - 例外処理

これはうまくいきますが、サーバーがHibernateSystemExceptionをスローすると、Springはそれをシリアル化してワイヤを介してクライアントに返します。これはクライアントがクラスパスに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

は、私はあなたが適切HTTPInvokerを使用している場合、あなたはあなたの顧客が自分のクラスパスにHibernateSystemExceptionを持っている必要はありませんが、私は、彼らが必要と主張したい理解することができます。これはサービスファサード/インターフェース層になるようには設計されていません.RMIの代わりにHTTPを使用して、リモートJVM上でJavaメソッドを実行できるようにするだけです。

クライアントがHibernateに依存することを本当に望んでいない場合は、try/catchブロックを使用してください。 (私はそれを反対して論争しているので、デバッグに苦労するでしょう:あなたのスタックトレースはクライアントとサーバーの間で分割されます)。

私は自分で使ったことがありませんが、try/catchを全面に追加するのではなく、1つの場所で特定の例外をキャッチするために、org.springframework.remoting.support.RemoteExporter.setInterceptors(Object[])メソッドを追加することができます。

0

DAOの前にあるFacadeレイヤのtry/catchは、返す例外を完全に制御するために、と正確にはです。私はそれが最初は醜いと感じることに同意するが、それはクライアントとDAOの間の重要な層だと私の意見では思う。

ストアデータAPI呼び出しの結果(作業済みではない)とエラーメッセージを伝達するために、void戻り値の型を使用するのではなく、ある種のOperationStatusオブジェクトを返すことさえあります。

0

は、私は、ソリューションの使用しましたN1H4Lと同様であるが、AspectJである。

まず私は、クライアントが(私の場合には、サービス・インターフェースとのDTOと瓶でのRuntimeExceptionの非常に単純なサブクラスである)クラスBusinessExceptionを拡張するのを意識したいすべての例外を作りました。

私はクライアントにサービスの内部について多くのことを知らせたくないので、私はちょうど "内部サーバーエラー"と言います。

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のを使用していました注釈@Transactionalデータベースへの接続が失敗したとき。 しかし、RmiServiceExporterのメソッドsetInterceptorは完全に機能します。

関連する問題