Best practicies in EJB exception handling

■before all
あれこれ始める前に ↓をクリアにしといて
・エンティティBeanとセッションBeanの違い
・bean-managed persistence (BMP) と container-managed persistence (CMP)の違い


■例外ハンドリングの一般的な決まりごと
1. ハンドリング出来んのならcatchせんこと.
2. catchした例外は揉み消さないこと.
3. なるたけ例外発生箇所に近いとこでcatchすること
4. catchしたとこでログること 出なければ再throwすること
5. structure your methods according to how fine-grained your exception handling must be.
6. use as many typed exceptions as you need, particularly for application exceptions.

+ it is a trade-off b/w how close to the source you catch an exception (point 3) and how far you let it fall before you've completely lost the intent or content of the original exception.


□log4j
log4j has three main components: layout, appender and category.
layout
represents the format of the message to be logged.
appender
is an alias for the physical location at which the message will be logged.
category
is the named entity; you can think of it as a handle for logging.

every category comes with its own layout and appender difinitions.


■exception categories
the ejb spec classifies exceptions into three broad categories:
+ JVM exceptions:
 this type of exception is thrown by the JVM.
 there is nothing you can do about JVM exceptions.
 they indicate a fatal situation.
 the only graceful exit is to stop the application server,
 maybe beef up the hardware resources, and restart the system.
 ex. OutOfMemoryError
+ Application exceptions:
 an application exception is a custom exception thrown by the application or a third-party library.
 these are essentially checked exceptions;
 they denote that some condition in the business logic has not been met.
 under these conditions,
 the caller of the EJB method can gracefully handle the situation and take an alternative path.
+ System exceptions:
 most often system exceptions are thrown as subclasses of RuntimeException by the JVM.
 a NullPointerException, or an ArrayOutOfBoundsException, for ex, will be thrown due to a bug in the code.
 another type of system exception occurs
 when the system encounters an improperly configured resource such as a missplelled JNDI lookup.
 in this case, it will throw a checked exception.
 the rule of thumb is, if there isnt anything you can do about an exception, it's a system exception and it should be thrown as an unchecked exception.
 
 note:
 a checked exception
  is a java class that subclasses java.lang.Exception.
  by subclassing java.lang.Exception, you are forced to catch the exception at compile time.
 unchecked exception
  is one that subclasses java.lang.RuntimeException.
  subclassing java.lang.RuntimeException ensures you will not be forced by the compiler to catch the exception.
 
 
■How the EJB container handles exceptions
EJBコンテナが、EJBコンポーネントの全メソッドを呼ぶ。
つまり、例外も、EJBコンテナが呼ぶ。
EJBが扱う例外は2種類のみ:
application exceptionとsystem exception。

+application exception
any exception declared on the method signatures in the remote interface (other than RemoteException).
基本的に業務のワークフローで生じるもの。
application exceptionは、RuntimeExceptionやそのサブクラスを継承すべきでない。
when an application exception occurs, the EJB container doesnt roll back the transaction unless it is asked to do so explicitly, with a call to the setRollbackOnly() method on the associated EJBContext object.
application exceptionはクライアントに届けられるべきもの。
EJBコンテナは決してapplication exceptionをラップしたりしない。

+system exception
cheched exceptionかunchecked exceptionのどっちかで
EJBメソッドがリカバー出来ないもの。
EJBコンテナがunchecked例外をinterceptすると、
トランザクションをロールバックして必要に応じてクリーンアップする。
それからunchecked例外をRemoteExceptionでラップして
クライアントにthrowする。
つまり、
EJBコンテナがクライアントに投げるunchecked system exceptionは
全部RemoteExceptionか、そのサブクラスゆうこと。

EJBコンテナをinternal housekeepingとして使うんなら
checked例外をunchecked例外としてthrowせんといけんくなるやろう。
どこで発生したsystem exceptionだろうが、
javax.ejb.EJBExceptionかそのサブクラスで、
オリジナル例外をラップして投げる感じ。
なぜなら、
EJBException自身はunchecked例外で、
there is no need to declare it in the throws clause of the method.
EJBコンテナは、EJBExceptionかサブクラスをcatchして、
RemoteExceptionにラップしてクライアントにthrow。

※EJB1.1以降は、EJB実行クラスはRemoteExceptionを投げないこと。

※RemoteException ex ... java.lang.Exception
※EJBException ex java.lang.RuntimeException


■Common exception-handling strategies
複数の開発者が好き勝手に例外ハンドリングしては保守が大変。
同じ例外でも、発生箇所次第で、異なるハンドリングが必要になりうるもの。
その辺をきちんとせんと、ログ出力が散在してまう。
ideally, exception logging should occur in as few places as possible w/o compromising the content.


■EJB exception-handling heuristics
in good EJB design,
clients never invoke methods on entity EJB components.
基本的にエンティティEJBは、セッションEJBが呼ぶもの。
きちんとセッションEJBが呼んでる場合は、例外ログもセッションBeanで。
クライアントが直接エンティティEJBを呼んでしまってる場合は、
クライアントだけやなく、エンティティEJBでもログ出力すべし。

クライアントに呼ばれたエンティティEJBはセッションEJBからも呼ばれうる。
つまり、ログの重複が生じうる。

解決法↓
・the extent of planned code reuse:
 複数ヶ所でのログ出力が問題。ログ出力箇所を減らすデザインに直す。
・the type of clients you want to serve:
 it is important to consider whether you will be serving a J2EE Web tier, stand-alone java applications, PDAs, or other clients.
・the type of exception (app or sys) youll be dealing with:
 handling application exception is significantly different from handling system exceptions.
 it's best to handle an original exception by wrapping it.
 on the other hand,
 application exception are explicitly thrown by the EJB developer, often by wrapping a message. because the intent of an application exception is clear, there is no reason to preserve its context. this type pf exception need not be logged in the EJB tier or the client tier.


■handling application exceptions
nth special discovery...


■handling system exceptions
entity EJB components encounter
- RemoteExceptions when they refer to other EJB remote interfaces
-NamingExceptions while looking up other EJB components
-SQLException if they use bean-managed persistence (BMP).
上記のような例外らは、EJBExceptionにラップされてcatchなりthrowなりされるべき。


■avoiding duplicate logging
一般的に、例外ログは、セッションBeanで出力されるもの。
だが、エンティティBeanが直接他の層のBeanにアクセスしたりすると、エンティティBeanでも例外をログったり投げたりせんといけんくなる。
the problem here is that the caller has no way of knowing that the exception has been logged and will likely log it again, which will result in duplicate logging.
more importantly, the caller has now way of accessing the unique ID generated during the initial logging. any logging w/o the mechanism to cross-reference is no good.

solution:
fortunately, addressing there problems is fairly easy to do in a generic way. all you need is a mechanism for the caller to:
-access the unique ID
-find out if the exception has already been logged
ex:
public class LoggableEJBException extends EJBException {
 //flg to check if the exception has been logged.
 protected boolean isLooged = false;
 protected String uniqueID = null;
 public LoggableEJBException (Exception e) {
  super(e);
  // generate a ID using the current time and host name of the machine.
  uniqueID = ExceptionIDGenerator.getExceptionID();
 }...
}

note code:
} catch (RemoteException re) {
 Throwable t = re.detail;
 if ( t != null && t instanceof Exception) {
  throw new LoggableEJBException ( (Exception) re.detail );
 } else {
  throw ne LoggableEJBException(re);
 }
}


■System exception in session EJB components
there is one way that session EJB components might handle exceptions differently from entity EJB components:
because
most EJB systems are accessible only from the Web tier and session EJB can serve as a facade to the EJB tier.
it's different because in a RemoteException, the actual exception will be stored in a public attribute called detail (which is of type Throwable). most of the time, this public attribute holds an esception. if you call a printStackTrace on a RemoteException, it prints the stack trace of exception itself, in addition to the stack trace of the detail. you dont need the stack trace of the RemoteException as such!!


■[TIPS] StackTraceUtil
a StachTraceUtil can help you to lconvert stack traces into Strings and log the Strings.
(log4j can only log the String messages.)

public class StrackTraceUtil {
 public static String getStackTrace(Exception e) {
  StringWriter sw = new StringWriter();
  PrintWriter pw = new PrintWriter(sw);
 }
 ...
}

note:
デフォルトのjava.lang.Throwable#printStachTrace()は、
system.err.throwableにログ出力するのと、
オーバーロードされたprintStackTrace()を持つ。
printStackTrace()は、PrintWriter か PrintStreamにログる。
上のStackStraceUtilのメソッドは、StringWriterをPrintWriterでラップしてるから、
PrintWriterが呼ばれた時、PrintWriterはスタックトレースを保持してる、ゆうわけ。
つまり、
StringWriterのtoString呼ぶだけで、stack trace情報が取得できちゃうのだ。


ref:
Best practicies in EJB exception handling
Srikanth Shenoy, 01 May 2001
http://www.ibm.com/developerworks/library/j-ejbexcept.html

tag : EJB TIPS

2009-04-20 22:29 : __j2ee__ejb : コメント : 0 : トラックバック : 0 :
コメントの投稿
非公開コメント

« next  ホーム  prev »

search

ad



counter


tag cloud

category cloud