WebSphere Application Server, Version 6.0.x   
             オペレーティング・システム: AIX , HP-UX, Linux, Solaris, Windows

             目次と検索結果のパーソナライズ化

例: データ・アクセス例外の処理 - StaleConnectionException

次のコード・サンプルを使用して、異なるトランザクション・シナリオで異なるタイプのデータ・アクセス・クライアントの不整合な接続例外をプログラマチックに処理する方法について説明します。

アプリケーションがデータベース操作で不整合な接続例外を受け取る場合、 これは現在保留されている接続が有効ではなくなったことを示しています。 どのデータベース操作においても不整合な接続例外を入手する可能性はあ りますが、発行された不整合な接続例外を確かめるのに最も一般的なのは、 接続を検索した直後に、その接続を初めて使用するときです。 接続がプールされているため、プールからの検索直後の操作、 すなわちデータベースとの最初の通信が試行されるまで、データベースの障害は検出されません。 接続の失効がマークされるのは、障害が検出されたときだけです。 データベースにアクセスする各メソッドがプールから新しい接続を取得するときは、不整合な接続例外の発生頻度は低くなります。

不整合な接続例外の原因の多くは、 データベース・サーバーのネットワークにかかわる偶発的な問題です。 新規の接続を取得して操作を再試行すれば、エンド・ユーザーに例外が表示されることなく、 操作は正常に終了します。 場合によっては、再試行と再試行の間にわずかな待機時間を追加して、 データベース・サーバーのリカバリーの時間を長くすると有益な場合もあります。 ただし、データベースが延長時間の間にダウンする場合は、 アプリケーションが操作を無制限に再試行することがないようにしてください。

アプリケーションが操作を再試行するために新規接続を取得する場合は、 その前に元の接続が関係していたトランザクションをロールバックして、 新しいトランザクションを開始してください。 このアクションに関する詳細は、次の 2 つのカテゴリーに分類できます。

データベース・アクセスと同じメソッドで開始される Bean 管理のグローバル・トランザクション・コンテキストで作動するオブジェクト。
サーブレットあるいは Bean 管理トランザクション (BMT) を持つセッション Bean は、 ネーミング・オブジェクトまたは Bean の EJBContext オブジェクトから検索可能な javax.transaction.UserTransaction オブジェクトの begin() を呼び出すことによって、 グローバル・トランザクションを明示的に開始できます。 Bean 管理トランザクションをコミットする場合、 アプリケーションは UserTransaction オブジェクトの commit() を呼び出します。 トランザクションをロールバックする場合、アプリケーションは rollback() を呼び出します。 エンティティー Bean および BMT を持たないセッション Bean の場合は、 明示的にグローバル・トランザクションを開始することができません。

Bean 管理トランザクションを明示 的に開始したオブジェクトが、データベース操作で不整合な接続例外を受け取る場合は、 接続を閉じ、トランザクションをロールバックしてください。この時点で、アプリケーション開発者は、 新規のトランザクションを開始して、新規の接続を取得し、操作を再試行することを決定することができます。

以下のコード・フラグメントは、 このシナリオにおける不整合な接続例外の処理例を示しています。
//get a userTransaction
javax.transaction.UserTransaction tran = getSessionContext().getUserTransaction();
//retry indicates whether to retry or not
//numOfRetries states how many retries have
// been attempted
boolean retry = false;
int numOfRetries = 0;
java.sql.Connection conn = null;
java.sql.Statement stmt = null;
do {
  try {
    //begin a transaction
    tran.begin();
    //Assumes that a datasource has already been obtained
    //from JNDI
    conn = ds.getConnection();
    conn.setAutoCommit(false);
    stmt = conn.createStatement();
    stmt.execute("INSERT INTO EMPLOYEES VALUES
              (0101, 'Bill', 'R', 'Smith')");
    tran.commit();
    retry = false;
  } catch(com.ibm.websphere.ce.cm.StaleConnectionException sce) {
//if a StaleConnectionException is caught
    // rollback and retry the action
    try {
      tran.rollback();
    } catch (java.lang.Exception e) {
      //deal with exception
      //in most cases, this can be ignored
    }
    if (numOfRetries < 2) {
      retry = true;
      numOfRetries++;
    } else {
      retry = false;
    }
  } catch (java.sql.SQLException sqle) {
    //deal with other database exception
    retry = false
  } finally {
    //always cleanup JDBC resources
    try {
      if(stmt != null) stmt.close();
    } catch (java.sql.SQLException sqle) {
      //usually can ignore
    }
    try {
      if(conn != null) conn.close();
    } catch (java.sql.SQLException sqle) {
      //usually can ignore
    }
  }
} while (retry) ;
グローバル・トランザクション・コンテキストおよびトランザクションで作動するオブジェクトが、 データベース・アクセスと同じメソッドで開始されない
コンテナー管理トランザクションの場合のように、不整合な接続例外 を受け取るオブジェクトが、トランザクションに対して直接制御を行わない 場合、そのオブジェクトはトランザクションにロールバックをマークし、続 いてその呼び出し元にトランザクションを再試行するように指示します。 ほとんどの場合、これは、その操作を再試行するように指示するアプリケ ーション例外を作成することによって実行できます。 ただし、このアクションがいつも可能なわけではありません。 多くの場合は、メソッドは特定の例外を作成するようにしか定義されていません。 エンタープライズ Bean 上の ejbLoad() および ejbStore() メソッドの場合がこれに該当します。 次の 2 つの例で、上記のシナリオをそれぞれ説明します。
例 1: データベース・アクセス・メソッドがアプリケーション例外を作成します
データベースにアクセスするメソッドが、必要な例外をどれでも自由に作成できる場合、 不整合な接続例外をキャッチして、 メソッドの再試行を示すアプリケーション例外を作成することが最適な方法です。 次の例は、トランザクション区分 TX_REQUIRED を用いてエンティティー Bean のメソッドを呼び出す EJB クライアントを示したものです。 TX_REQUIRED は、insertValue() を呼び出したときに、 グローバル・トランザクションがコンテナーによって開始されるということを示します。
public class MyEJBClient {
//... other methods here ...
public void myEJBClientMethod()
{
MyEJB myEJB = myEJBHome.findByPrimaryKey("myEJB");
boolean retry = false;
do {
try {
retry = false;
myEJB.insertValue();
}
catch(RetryableConnectionException retryable) {
retry = true;
}
catch(Exception e) { /* handle some other problem */ }
} while (retry);
}  
}  //end MyEJBClient

public class MyEJB implements javax.ejb.EntityBean {
//... other methods here ...
public void insertValue() throws RetryableConnectionException,
java.rmi.EJBException {
try
{
conn = ds.getConnection();
stmt = conn.createStatement();
stmt.execute("INSERT INTO my_table VALUES (1)");
}
catch(com.ibm.websphere.ce.cm.StaleConnectionException
sce) {
getSessionContext().setRollbackOnly();
throw new RetryableConnectionException();
}
catch(java.sql.SQLException sqle) {
//handle other database problem
}
finally {
21
//always cleanup JDBC resources
try {
if(stmt != null) stmt.close();
} catch (java.sql.SQLException sqle) {
//usually can ignore
}
try {
if(conn != null) conn.close();
} catch (java.sql.SQLException sqle) {
//usually can ignore
}
}
}
}  //end MyEJB

MyEJBClient は最初に MyEJB Bean をホーム・インターフェースから取得します。MyEJB Bean はあらかじめ Java Naming and Directory Interface (JNDI) から取得されているものとします。 次に MyEJB Bean は、Bean 上の insertValue() を呼び出します。Bean 上のこのメソッドは、接続を取得し、値を表に挿入しようとします。 これらのメソッドのいずれかが失敗して不整合な接続例外を発生した場合、 そのメソッドはトランザクションに rollbackOnly のマークを付けて (呼び出し元に、 このトランザクションのロールバックを強制します)、新たに 再試行可能接続例外を作成し、 例外がスローされる前にリソースをクリーンアップします。 再試行可能接続例外は、アプリケーション定義の例外にすぎず、 メソッドの再試行を呼び出し元に知らせます。 呼び出し元は、再試行接続例外をモニターして、 これをキャッチした場合にはメソッドを再試行します。 この例では、コンテナーがトランザクションの開始および終了を行うため、 クライアントまたはサーバーでのトランザクション管理は必要ありません。 もちろん、クライアントがトランザクションのコミットあるいはロールバックも実行していれば、 クライアントによって Bean 管理トランザクションが開始され、動作はこの後も同じとなります。

例 2: データベース・アクセス・メソッドが onlyRemote 例外または EJB 例外を作成します。
アプリケーションで定義した例外をスローすることが、すべてのメソッドに許可されているわけではありません。 Bean 管理パーシスタンス (BMP) を使用している場合は、 ejbLoad() および ejbStore() メソッドを使用して Bean の状態を保管します。こ れらのメソッドから発行される例外は java.rmi.Remote 例外または javax.ejb.EJB 例外のみであり、前述の例と同じような例外は使用できません。
コンテナー管理パーシスタンス (CMP) を使用している場合は、コンテ ナーが Bean パーシスタンスを管理します。不整合な接続例外を認識するのもコンテナーです。 不整合な接続が検出された場合、例外はクライアントに戻されるまでリモート例外であるため、 単純な catch ブロック 1 つのみでは不十分です。 リモート例外の根本的原因が不整合な接続例外かどうかを判別する方法があります。リモート例外が作成されて別 の例外をラップすると、元の例外は通常は保存されます。 すべてのリモート例外インスタンスには詳細プロパティーがあり、その型は java.lang.Throwable です。 この詳細を使用すれば、元の例外までトレースバックでき、それが不整合 な接続例外であれば、トランザクションを再試行できます。 実際には、これらのリモート例外のいずれかが Java 仮想マシン API から次の Java 仮想マシン API に流れるときに詳細が失われるため、 トランザクションの開始はデータベース・アクセスが行われるのと同じサーバーで開始する方が適切です。 この理由から、 以下の例では Bean 管理トランザクション境界を持つセッション Bean がアクセスするエンティティー Bean を示します。
public class MySessionBean extends javax.ejb.SessionBean {
... other methods here ...
public void mySessionBMTMethod() throws
java.rmi.EJBException
{
javax.transaction.UserTransaction tran =
getSessionContext().getUserTransaction();
boolean retry = false;
do {
try {
retry = false;
tran.begin();
// causes ejbLoad() to be invoked
myBMPBean.myMethod();
// causes ejbStore() to be invoked
tran.commit();
}
catch(java.rmi.EJBException re) {
try { tran.rollback();
}
catch(Exception e) {
//can ignore
}
if (causedByStaleConnection(re))
retry = true;
else
throw re;
}
catch(Exception e) {
// handle some other problem
}
finally {
//always cleanup JDBC resources
try {
if(stmt != null) stmt.close();
} catch (java.sql.SQLException sqle) {
//usually can ignore
}
try {
if(conn != null) conn.close();
} catch (java.sql.SQLException sqle) {
//usually can ignore
}
}
} while (retry);
}

public boolean causedByStaleConnection(java.rmi.EJBException
EJBException)
{
java.rmi.EJBException re = EJBException;
Throwable t = null;
while (true) {
t = re.getCause();
try { re = (java.rmi.EJBException)t; }
catch(ClassCastException cce) {
return (t instanceof
com.ibm.websphere.ce.cm.StaleConnectionException);
}
}
}
}
public class MyEntityBean extends javax.ejb.EntityBean {
... other methods here ...
public void ejbStore() throws java.rmi.EJBException
{
try {
conn = ds.getConnection();
stmt = conn.createStatement();
stmt.execute("UPDATE my_table SET value=1 WHERE
primaryKey=" + myPrimaryKey);
}
catch(com.ibm.websphere.ce.cm.StaleConnectionException
sce) {
//always cleanup JDBC resources
try {
if(stmt != null) stmt.close();
} catch (java.sql.SQLException sqle) {
//usually can ignore
}
try {
if(conn != null) conn.close();
} catch (java.sql.SQLException sqle) {
//usually can ignore
}
// rollback the tran when method returns
getEntityContext().setRollbackOnly();
throw new java.rmi.EJBException("Exception occurred in
ejbStore", sce);
}
catch(java.sql.SQLException sqle) {
// handle some other problem
}
}
}
前の例の mySessionBMTMethod() では、以下のとおりです。
  • セッション Bean は最初に UserTransaction オブジェクトをセッション・コンテキストから検索し、続いてグローバル・トランザクションを開始します。
  • 次に、エンティティー Bean 上のメソッドを呼び出します。ここでは、ejbLoad() メソッドを呼び出します。ejbLoad() が正常に実行されると、クライアントがそのトランザクションをコミットするため、 ejbStore() メソッドが呼び出されます。
  • ejbStore() では、エンティティー Bean が接続を取得し、接続の状態をデータベースに書き込みます。取得した接続が不整合である場合、トランザクションには rollbackOnly のマークが付けられ、StaleConnectionException をラップする新規の EJBException がスローされます。 そして、その例外をクライアントがキャッチすると、JDBC リソースをクリーンアップし、 トランザクションをロールバックして、causedByStaleConnection() を呼び出します。 これにより、不整合な接続例外がその例外に埋め込まれているかどうかを判別しま す。
  • このメソッドが true を戻した場合、retry フラグが設定されてトランザクションが再試行されます。 そうではない場合、例外を呼び出し元に再発行します。
  • causedByStaleConnection() メソッドは、detail 属性のチェーンを調べて、元の例外を検索します。 例外が最後にクライアントに戻されるまでには、例外が多重ラッピングされている可能性もあるため、 メソッドは非リモート例外を検出するまで検索を続けます。 この最終の例外が不整合な接続例外であれば、検出されて true が戻 されます。そうではない場合は、(不整合な接続例外がリモート例外として 発行されることは決してないため) リストに不整合な接続例外がない ことになり、false が戻されます。
  • ここで BMP Bean ではなく CMP Bean と通信する場合も、セッション Bean はまったく同じです。 CMP Bean の ejbStore() メソッドは空であることがほとんどで、 コンテナーはこのメソッドを呼び出した後、生成済みコードを持つ Bean を持続させます。
  • 不整合な接続例外がパーシスタンス中に発生した場合、この例外はリモート例外にラップされ、 呼び出し元に戻されます。 causedByStaleConnection() メソッドはもう一度例外チェーン全体を調べて、 ルートの例外すなわち不整合な接続例外を検索します。
ローカル・トランザクション・コンテキストで作動するオブジェクト。
データベース操作がグローバル・トランザクション・コンテキストの外部で行われると、 コンテナーは暗黙的にローカル・トランザクションを開始します。 このオブジェクトには、指定されていないトランザクション・コンテキス トで稼働するエンタープライズ Bean に加えて、トランザクションを開始する際に UserTransaction インターフェースを使用しないサーブレットまたは JSP も組み込まれています。 グローバル・トランザクションの場合と同様、ローカル・トランザクションもロールバックしてからでなければ操作を再試行することはできません。 上記の場合、ローカル・トランザクション内包は、通常、ビジネス・メソッドの終了時に終了します。 アクティビティー・セッションを使用している場合は例外です。 この場合、アクティビティー・セッションは、新規の接続を取得する前に終了しなければなりません。

ローカル・トランザクションが未指定のトランザクション・コンテキスト内で稼働するエンタープライズ Bean で発生すると、エンタープライズ Bean クライアント・オブジェクトは、ローカル・トランザクション内包の外側で、直前の黒丸で説明したメソッドを使用して、トランザクションを再試行します。ただし、ローカル・トランザクショ ン内包がサーブレットまたは JSP ファイルの一部として実行される場合に、操作を再試行できるクライアント・オブジェクトはありません。 このため、ユーザー・トランザクションに含まれる場合を除き、 サーブレットや JSP ファイルではデータベースの操作を実行しないほうがよいでしょう。




サブトピック
Linux システムの場合の StaleConnectionException
例: サーブレット JDBC 接続例外の処理
例: コンテナー管理データベース・トランザクションのセッ ション Bean 用の接続例外の処理
例: Bean 管理データベース・トランザクシ ョンのセッション Bean 用の接続例外の処理
例: コンテナー管理データベース・トランザクションの BMP Bean 用の接続例外の処理
参照トピック    

ご利用条件 | フィードバック

最終更新: Jan 22, 2008 12:07:38 AM EST
http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.base.doc/info/aes/ae/rdat_stalconexp.html