ヘッダーをスキップ
Oracle Database Java開発者ガイド
10gリリース2(10.2)
B19189-01
  目次へ
目次
索引へ
索引

前へ
前へ
次へ
次へ
 

2 Oracle DatabaseでのJavaアプリケーション

Oracle Databaseでは、標準のJavaアプリケーションが実行されます。ただし、Javaを統合したOracle Database環境は、一般的なJava開発環境とは異なります。この章では、Oracle DatabaseでのJavaアプリケーションの作成、インストールおよび配布について、一般的なJava開発環境との基本的な相違について説明します。この章の内容は、次のとおりです。

2.1 Javaアプリケーションにインポーズされるデータベース・セッション

Javaを統合したOracle Databaseでは、Javaアプリケーションはデータベース・セッションのコンテキスト内に存在します。OracleのJava仮想マシン(JVM)セッションは、従来のOracleセッションとまったく同様です。Oracle JVMの各セッションは、セッション内の複数のコールにわたって、クライアントがアクセスするJavaアプリケーションの状態を保持します。

図2-1に、各Javaクライアントが、データベース内のJavaアプリケーションを実行する環境としてデータベース・セッションを起動する方法を示します。Javaの各データベース・セッションには、独立したガベージ・コレクタ、セッション・メモリーおよびコール・メモリーがあります。

図2-1 各データベース・セッション内のJava環境

各データベース・セッション内のJava環境
画像の説明

セッションのコンテキストで、クライアントは次の内容を実行します。

  1. データベースに接続してセッションをオープンします。

  2. データベース内でJavaを実行します。これはコールとも呼ばれます。

  3. 必要に応じてコールを実行し、セッション内で処理を続行します。

  4. セッションを終了します。

1つのセッション内で、クライアントには独自のJava環境があります。クライアントには、セッションごとに個別のJVMが個々に起動されるように見えますが、実際の実装はそれよりも効率的です。セッション内では、Oracle JVMがアプリケーションのスケーラビリティを管理します。あるクライアントからのすべてのコールは、そのクライアントのセッションで管理され、各クライアントからのコールは個別に処理されます。 Oracle JVMは、クライアント間の読取り専用データの共有を最大にし、セッションごとの増分フットプリントを最小限にすることで、複数のクライアントのパフォーマンスを最大にします。

基盤となるサーバー環境では、セッション、ネットワーク、状態および他の共有リソース管理の問題に関する詳細が、Javaコードに対して明示されません。 staticとして定義された変数は、クライアントに対してローカルです。メモリーはセッションの境界を超えて使用できないため、クライアントは他のクライアントのstatic変数にはアクセスできません。各クライアントは、それぞれのセッション内でJavaアプリケーション・コールを実行するため、各クライアントのアクティビティは他のクライアントとは切り離されています。コール中はオブジェクトを異なるクラスのstaticフィールドに格納でき、次のセッションでもこの状態で使用できます。Javaプログラムの状態はすべてプライベートで、セッション全体にわたって存続します。

Oracle JVMは、セッション内で次の内容を管理します。

2.2 実行制御

Java2 Platform, Standard Edition(J2SE)環境では、main()メソッドを使用してJavaアプリケーションを開発します。このメソッドは、クラスの実行時にインタプリタからコールされます。main()メソッドは、コマンドラインで次のコマンドを入力したときにコールされます。

java classname

このコマンドは、Javaインタプリタを起動し、実行対象のクラス(つまり、classnameで指定されたクラス)をJavaインタプリタに渡します。インタプリタがmain()をコールすることで、クラスがロードされ、アプリケーションの実行が開始されます。ただし、main()メソッドをコールしても、データベース内のJavaアプリケーションは起動されません。

データベース内のJavaアプリケーションをロードした後は、ロードされたクラス内の任意のstaticメソッドをコールすることでJavaコードを実行できます。実行するには、クラスやメソッドが公開されている必要があります。Oracle Databaseの場合、main()はJavaアプリケーションのエントリ・ポイントとはみなされません。Javaアプリケーションを実行するときは、ロードされたクラス内のメソッド名をエントリ・ポイントとして指定します。

たとえば、通常のJava環境では、次のコマンドを実行することでサーバーのJavaオブジェクトを起動します。

java myprogram

myprogramには、main()メソッドが組み込まれているクラスの名前を指定します。myprogramでは、main()がただちにmymethod()をコールして、着信情報を処理します。

Oracle Databaseでは、データベースにmyprogram.classファイルをロードし、エントリ・ポイントとしてmymethod()公開します。これで、クライアントまたはトリガーによって、明示的にmymethod()が起動されます。

2.3 Javaのコード、バイナリおよびリソースの格納

標準的なJava開発環境では、Javaのソース・コード、バイナリおよびリソースは、次のようにファイルとしてファイル・システムに格納されます。

また、Javaアプリケーションを実行する場合は、CLASSPATHを設定し、.classファイルが格納されているファイル・システムのファイルまたはディレクトリ・パスを指定します。Javaでは、これらのファイルをZIPファイルやJavaアーカイブ(JAR)ファイルなどの単一のアーカイブ形式にまとめることもできます。

これらの概念は、いずれもOracle Database環境では異なります。表2-1に、Oracle DatabaseによるJavaクラスの処理方法と依存クラスの位置の特定方法を示します。

表2-1 Javaのコードとクラスの説明

Javaのコードとクラス 説明
Javaのコード、バイナリおよびリソースの格納 Oracle Databaseの場合、ソース・コード、クラスおよびリソースはデータベース内に常駐するため、Javaスキーマ・オブジェクトと呼ばれます。このオブジェクトでは、1つのスキーマが1つのデータベース・ユーザーに対応しています。Javaスキーマ・オブジェクトには、ソース、クラスおよびリソースの3つのタイプがあります。.java.class.sqlj.propertiesまたは.serファイルはサーバー上にありません。これらのファイルは、適切なJavaスキーマ・オブジェクトにマップされます。
Javaクラスの位置の特定 Javaのソース、クラスおよびリソースのスキーマ・オブジェクトを検索するには、CLASSPATHのかわりに、リゾルバを使用して1つ以上のスキーマを指定します。

データベースにロードされるJavaクラス

Oracle JVMでJavaファイルを使用するには、そのJavaファイルをスキーマ・オブジェクトとしてデータベースにロードする必要があります。図2-2に示すように、loadjavaユーティリティを使用してOracle JVMのJavaコンパイラをコールできます。このコンパイラは、ソース・ファイルを標準のクラス・ファイルにコンパイルします。

また、図2-2では、loadjavaを使用して、システム・データベース表に格納されているオプションの値を設定できることを示しています。これらのオプションは、特にJavaソース・ファイルの処理に影響を与えます。

図2-2 Oracle DatabaseへのJavaのロード

Oracle DatabaseへのJavaのロード
画像の説明

各Javaクラスは、スキーマ・オブジェクトとして格納されます。オブジェクトの名前は、含まれているパッケージの名前も含めたクラスの完全修飾名から導出されます。たとえば、Handleクラスのフルネームは、次のようになります。

oracle.aurora.rdbms.Handle

Javaスキーマ・オブジェクト名では、ピリオドがスラッシュで置換されるため、前述のクラスのフルネームは、次のようになります。

oracle/aurora/rdbms/Handle

Oracle Databaseでは、最大4000文字までのJavaの名前を受け入れます。 ただし、Javaスキーマ・オブジェクトには31文字を超える名前は指定できません。したがって、スキーマ・オブジェクト名に31文字を超える名前が指定された場合は、スキーマ・オブジェクトの短縮名または別名が生成されます。名前が31文字以下の場合は、完全修飾名(フルネームとも呼ばれる)が使用されます。フルネームは、必要に応じて任意のコンテキストで指定できます。必要な場合は、名前のマッピングがOracle Databaseによって処理されます。

2.4 Javaクラス・メソッドの実行準備

Javaメソッドを実行するには、次の準備が必要です。

  1. Javaソース・コードをいつコンパイルするかを決定します。

  2. データベース内のJavaクラスの位置の特定に、デフォルトのリゾルバを使用するか、別のリゾルバを使用するかを決定します。

  3. クラスをデータベースにロードします。クラスにデフォルトのリゾルバを使用しない場合は、ロード・コマンドで別のリゾルバを指定してください。

  4. クラスまたはメソッドを公開します。

この項の内容は、次のとおりです。

2.4.1 Javaクラスのコンパイル

Javaソース・コードのコンパイルは、次のいずれかの方法で実行できます。

  • クライアント・システムでソースを明示的にコンパイルしてから、javacなどのJavaコンパイラを介してデータベースにロードできます。

  • データベースに対して、loadjavaユーティリティで管理されるロード・プロセス中にソースをコンパイルするように指定できます。

  • 実行時に動的なコンパイルを強制できます。


注意:

loadjavaを使用してコンパイルを実行する場合は、コンパイラ・オプションを指定できます。詳細は、「コンパイラ・オプションの指定」を参照してください。

この項の内容は、次のとおりです。

2.4.1.1 javacによるソースのコンパイル

javacなど、従来のJavaコンパイラを使用してJavaソース・コードをコンパイルできます。コンパイルが終了した後は、ソースではなく、コンパイル済バイナリをデータベースにロードします。通常は、データベースでデバッグするよりも、各自のシステムでJavaコードをデバッグする方が簡単なため、これは便利なオプションです。

2.4.1.2 loadjavaによるソースのコンパイル

ソース・ファイルのloadjava-resolveオプションを指定すると、次の処理が実行されます。

  1. ソース・ファイルがソース・スキーマ・オブジェクトとしてロードされます。

  2. ソース・ファイルがコンパイルされます。

  3. コンパイル済の.javaファイルに定義されている各クラスに対して、クラス・スキーマ・オブジェクトが作成されます。

  4. コンパイル済のコードがクラス・スキーマ・オブジェクトに格納されます。

Oracle Databaseによって、すべてのコンパイル・エラーがloadjavaユーティリティのログ・ファイルとUSER_ERRORSビューに書き込まれます。

2.4.1.3 実行時におけるソースのコンパイル

-resolveオプションを指定せずにデータベースにJavaソースをロードすると、実行時にクラスが必要になったときに、Oracle Databaseによってソースが自動的にコンパイルされます。ソース・ファイルは、ソース・スキーマ・オブジェクトにロードされます。

Oracle Databaseによって、すべてのコンパイル・エラーがloadjavaユーティリティのログ・ファイルとUSER_ERRORSビューに書き込まれます。

2.4.1.4 コンパイラ・オプションの指定

コンパイラ・オプションを指定するには、次の方法があります。

  • loadjavaのコマンドラインにコンパイラ・オプションを指定します。loadjavaには、encodingオプションを指定することもできます。

  • JAVA$OPTIONS表に永続コンパイラ・オプションを指定します。JAVA$OPTIONS表は各スキーマごとに存在します。コンパイルする度に、これらのオプションが使用されます。ただし、loadjavaコマンドにコンパイラ・オプションを指定すると、この表に定義されているオプションはオーバーライドされます。この方法でコンパイラ・オプションを指定する場合は、この表を独自に作成する必要があります。

デフォルトのコンパイラ・オプション

コンパイル対象のソース・スキーマ・オブジェクトに、JAVA$OPTIONS表のエントリもコマンドラインのオプション値も指定されていない場合は、次のデフォルト値が使用されます。

  • encoding=System.getProperty("file.encoding");

  • online=true

    このオプションは、SQLJ構文が含まれているJavaソースにのみ適用されます。

  • debug=true

    このオプションは、次のように指定した場合と同じです。

    javac -g
    
    

コマンドラインのコンパイラ・オプション

loadjavaencodingコンパイラ・オプションを指定すると、.javaファイルのエンコーディングが識別されます。このオプションは、JAVA$OPTIONS表内の一致する値をすべてオーバーライドします。この値は、次のように指定した場合と同じです。

javac -encoding

このオプションはソース・ファイルのロード時のみ有効です。

データベース表に指定されるコンパイラ・オプション

JAVA$OPTIONS表の各エントリには、オプションの設定を適用するソース・スキーマ・オブジェクトの名前が指定されています。複数行を使用すると、ソース・スキーマ・オブジェクトごとに異なるオプションを設定できます。

次のプロシージャとファンクションを使用することで、JAVA$OPTIONS表のエントリを設定できます。これらのプロシージャとファンクションは、データベース・パッケージDBMS_JAVAに定義されています。

PROCEDURE set_compiler_option(name VARCHAR2, option VARCHAR2, value VARCHAR2);

FUNCTION get_compiler_option(name VARCHAR2, option VARCHAR2) RETURNS VARCHAR2;

PROCEDURE reset_compiler_option(name VARCHAR2, option VARCHAR2);

次の表に、これらのメソッドのパラメータを示します。

表2-2 nameパラメータとoptionパラメータの定義

パラメータ 説明
name Javaパッケージ名、完全修飾されたクラス名または空の文字列が入ります。コンパイラによって、Javaソース・スキーマ・オブジェクトのコンパイルに使用するオプションがJAVA$OPTIONS表で検索された場合は、スキーマ・オブジェクトの完全修飾されたクラス名に最も近いnameの値の行が使用されます。値が空文字列のnameは、あらゆるスキーマ・オブジェクト名に一致します。
option optionパラメータは、onlineencodingまたはdebugです。

最初、スキーマにはJAVA$OPTIONS表はありません。JAVA$OPTIONS表を作成するには、DBMS_JAVAパッケージのjava.set_compiler_optionプロシージャを使用して値を設定します。このプロシージャは、表が存在しない場合に表を作成します。パラメータは一重引用符で囲みます。次に例を示します。

SQL> execute dbms_java.set_compiler_option('x.y', 'online', 'false');

表2-3は、JAVA$OPTIONSデータベース表のサンプルです。パターン一致規則は、表エントリに対してできるかぎり多くのスキーマ名を一致させるための規則です。パターン一致の名前解決度が高いスキーマ名がエントリとして適用されます。この表には、encodingオプションのエントリがないため、コンパイラはデフォルトまたはコマンドラインで指定された値を使用します。この表のonlineオプションは、次のようにスキーマ・オブジェクト名に対応付けられます。

  • 名前a.b.c.dは、a.b.c.dで始まるクラス名およびパッケージ名に一致します。このパッケージとクラスは、online=trueでコンパイルされます。

  • 名前a.bは、a.bで始まるクラス名およびパッケージ名に一致します。名前a.bは、a.b.c.dとは一致しません。このパッケージとクラスは、online=falseでコンパイルされます。

  • その他のパッケージとクラスは空文字列のエントリに一致し、online=trueでコンパイルされます。

表2-3 JAVA$OPTIONS表の例

名前 オプション 一致例
a.b.c.d online true
  • a.b.c.d

    : パターンに完全一致しています。

  • a.b.c.d.e

    : 先頭部分がパターンに完全一致しています。完全修飾名に一致する他の規則はありません。

a.b online false
  • a.b

    : パターンに完全一致しています。

  • a.b.c.x

    : 先頭部分がパターンに完全一致しています。この規則以外に一致する規則はありません。

空文字列 online true
  • a.c

    : 定義済の名前に一致するパターンはありません。デフォルトは空文字列規則です。

  • x.y

    : 定義済の名前に一致するパターンはありません。デフォルトは空文字列規則です。


2.4.1.5 自動再コンパイル

Oracle Databaseには、依存関係管理および自動作成機能が用意されています。これらの機能は、ソース・プログラムまたはバイナリ・プログラムが変更されると、これらのプログラムに依存しているソース・プログラムを透過的に再コンパイルします。次の例を考えてみます。

public class A
{
  B b;
  public void assignB()
  {
    b = new B()
  }
}
public class B
{
  C c;
  public void assignC()
  {
    c = new C()
  }
}
public class C
{
  A a;
  public void assignA()
  {
    a = new A()
  }
}

システムは詳細なクラス・レベルで依存関係を追跡します。この例では、ABのインスタンスを保持し、BCのインスタンスを保持し、CAのインスタンスを保持しているため、クラスABおよびCが相互に依存しています。クラスAに新しいフィールドを追加して、このクラスの定義を変更すると、Oracle Databaseの依存関係メカニズムによって、クラスBCが無効になったことを示すフラグが設定されます。これらのクラスを次に使用する前に、Oracle Databaseは必要に応じて、これらのクラスを解決して、再コンパイルを試みます。クラスは、ソース・ファイルがサーバーにある場合にのみ再コンパイルされます。

この依存性のあるシステムによって、クラス間の依存関係がOracle Databaseで管理され、解決と再コンパイルが自動的に実行されます。開発作業中に問題を早期に特定する必要がある場合にのみ、コンパイルと解決を強制的に実行する必要があります。loadjavaユーティリティには、依存関係管理機能を利用しない場合に、コンパイルと解決を強制的に実行する機能もあります。

2.4.2 クラス依存関係の解決

Javaクラスの多くには、他のクラスへの参照が含まれています。これによって、コードの再利用が可能になります。従来のJVMは、CLASSPATHで指定されたディレクトリ内で.class.zipおよび.jarファイルを検索します。一方、Oracle JVMはデータベース・スキーマでクラス・オブジェクトを検索します。Oracle Databaseでは、すべてのJavaクラスがデータベースにロードされるため、あるJavaクラスが依存しているクラスをデータベース内で検索するには、検索場所を指定する必要があります。

データベースにロードされるすべてのクラスは、クラス・スキーマ・オブジェクトと呼ばれ、特定のスキーマにロードされます。 java.lang.*など、事前に定義されているJavaのApplication Programming Interface(API)はすべて PUBLICスキーマにロードされます。あるクラスが、事前に定義した他のクラスに依存している場合は、すべてのクラスをスキーマにロードすることになります。たとえば、スキーマがSCOTTである場合、データベース・リゾルバは、PUBLICスキーマを検索する前にSCOTTスキーマを検索します。 検索するスキーマのリストはリゾルバ仕様と呼ばれます。 リゾルバ仕様は各クラスごとに定義されます。CLASSPATHがすべてのクラスに対してグローバルな、従来のJVMとは対照的です。

クラス間の依存関係を検索および解決する場合、リゾルバは、相互に依存するすべてのクラスを検出したかどうかによって、各クラスに有効または無効のマークを付けます。ロードしたクラスに、適切なスキーマに存在しないクラスへの参照がある場合、そのクラスは無効としてリストされます。実行時に解決できない場合は、ClassNotFound例外が生成されます。また、クラスのツリーが大きすぎると、データベース・リソースが不足するため、実行時に解決できないことがあります。


注意:

Javaコンパイラと同様に、loadjavaはクラスへの参照を解決しますが、リソースへの参照は解決しません。クラスに必要なリソース・ファイルを正しくロードする必要があります。

クラス内の各クラス間参照の場合、リゾルバは、参照を満たす有効なクラス・スキーマ・オブジェクトについて、リゾルバ仕様に指定されているスキーマを検索します。 すべての参照が解決されると、リゾルバによりこのクラスは「有効」とマークされます。解決処理が行われていないクラス、または正常に解決できなかったクラスは「無効」とマークされます。無効にされたスキーマ・オブジェクトに依存するクラスも無効とマークされます。

依存クラスを簡単に検索できるように、Oracle Databaseには、定義者のスキーマを検索してからPUBLICスキーマを検索するデフォルトのリゾルバとリゾルバ仕様が用意されています。これによって、データベースにロードされたほとんどのクラスを検索できます。 ただし、各自またはPUBLIC以外のスキーマにロードされたクラスにアクセスする場合は、独自にリゾルバ仕様を定義する必要があります。

次の方法でクラスを解決できます。

  • 定義者のスキーマとPUBLICを検索するデフォルト・リゾルバを使用してロードします。

    loadjava -resolve
    
    
  • 独自のリゾルバ仕様定義を使用してロードします。

    loadjava-resolve -resolver "((* SCOTT)(* OTHER)(* PUBLIC))"
    
    

    この例では、リゾルバ仕様定義にSCOTTスキーマ、OTHERスキーマおよびPUBLICが指定されています。

-resolverオプションを使用して、定義されたスキーマ内で検索するオブジェクトを指定します。前述の例では、SCOTTOTHERおよびPUBLICで、すべてのクラス・スキーマ・オブジェクトが検索されます。スキーマで特定のクラスまたはクラス・グループのみを検索する場合は、検索範囲を絞り込むことができます。 たとえば、OTHERスキーマのmy/gui/*クラスのみを検索するには、次のようにリゾルバ仕様を定義します。

loadjava -resolve -resolver '((* SCOTT) ("my/gui/*" OTHER) (* PUBLIC))'

リゾルバ仕様の最初のパラメータには、クラス・スキーマ・オブジェクトを指定し、2番目のパラメータにはこれらのクラス・スキーマ・オブジェクトを検索するスキーマを定義します。

存在しないクラスへの参照の許可

リゾルバ仕様には、存在しないクラスへの未解決の参照を可能にする特別なオプションを指定できます。製品では内部クラスがまったく使用されないこともあります。通常のJava環境では、JVMはメソッドがコールされないかぎりメソッドを無視するため、問題は起こりません。ただし、Oracle Databaseのリゾルバは、未使用クラスも含めてJARファイル内の参照先のクラスをすべて解決しようとします。参照を有効にできない場合、JARファイル内のクラスは無効とマークされます。

参照を無視するには、リゾルバ仕様でワイルドカードのマイナス記号(-)を指定します。 次の例は、参照先のクラスがリゾルバ仕様のスキーマ・リストに存在しない場合にも、my/gui内のクラスへのすべての参照を許可するように指定しています。

loadjava -resolve -resolver '((* SCOTT) (* PUBLIC) ("my/gui/*" -))'

ワイルドカードを指定しないと、あるスキーマで依存クラスが見つからないと、そのクラスは無効としてリストされ、実行できなくなります。

また、検索できなかったクラスをすべて無視するように定義することもできます。ただし、依存クラスが見つからなかった場合にも、その依存クラスを必要としているクラスが有効としてマークされる可能性があるため、この方法は危険です。ただし、依存クラスを必要としているクラスは、依存クラスなしでは実行できません。この場合は、実行時に例外となります。

SCOTTまたはPUBLICに存在しないクラスをすべて無視するには、次のリゾルバ仕様を指定します。

loadjava -resolve -resolver "((* SCOTT) (* PUBLIC) (* -))"

後でロードするクラスがマイナス記号(-)ワイルドカードを含むリゾルバを必要とするような場合は、最初からそのようなリゾルバを使用しないでください。かわりに、参照先クラスをすべてスキーマに取り込んでから、解決してください。


注意:

存在しないクラスを処理する代替のメカニズムには、loadjava-gemissingオプションを使用します。このオプションを使用すると、loadjavaによって、参照される未定義のクラス定義が作成され、ロードされます。

バイトコード検証機能

JVMの仕様では、定義したクラスをJVMで使用するには、その前に.classファイルを検証する必要があります。Oracle JVMでは、クラスの解決時に検証が実行されます。リゾルバで次の問題が検出されると、該当するOracleエラー・コードが表示されます。

表2-4 ORAエラー

エラー・コード 説明
ORA-29545 リゾルバでクラスの形式が不正と判断されると、そのクラスは有効とマークされません。リゾルバでクラスが拒否されると、ORA-29545エラーが発行されます。このエラーはloadjavaユーティリティによってレポートされます。たとえば、.classファイルの内容がJavaコンパイルの結果に一致しない場合、またはファイルが破損している場合は、このエラーがスローされます。
ORA-29552 状況によっては実行時に例外をスローできるように、リゾルバでは、有効とマークしたクラスのバイトコードが変更されることがあります。このような場合、リゾルバはORA-29552警告を発行し、loadjavaがこのエラーをレポートします。Java言語仕様(JLS)でIncompatibleClassChangeErrorがスローされるように規定されている場合は、loadjavaユーティリティがこの警告を発行します。Oracle JVMは、リゾルバにこのような状況を検出させることで、JLSで規定されているランタイム動作をサポートします。

マイナス記号(-)ワイルドカードが指定されているリゾルバは、参照先クラスの有無に関係なく、クラスを有効とマークします。継承とインタフェースのために、あるクラスのインスタンスをスーパークラスまたは特定のインタフェースのインスタンスであるかのように使用する有効なJavaメソッドの作成が必要になる場合があります。検証対象のメソッドがクラスAへの参照をクラスBへの参照であるかのように使用している場合、リゾルバはABを拡張または実装しているかどうかを確認します。たとえば、次のメソッドについて考えてみます。シグネチャでは暗黙的にBのインスタンスを戻すことになっていますが、そのボディはAのインスタンスを戻します。

B myMethod(A a)
{
  return a;
}

このメソッドは、ABを拡張する場合、またはAがインタフェースBを実装している場合のみ有効です。AまたはBの解決にマイナス記号(-)ワイルドカードが使用された場合、リゾルバはこのメソッドの安全を判断できません。この場合、myMethodがコールされると、myMethodのバイトコードは例外をスローするバイトコードに置換されます。

ABのクラス定義が明確に識別されるスキーマにABがある場合、マイナス記号(-)ワイルドカードがないリゾルバは、そのABのクラス定義を検索して適切に解決します。代替リゾルバの使用は、JARファイルにない他の非システム・クラスを参照する既存のJARファイルのロードが必要な場合にのみ検討します。


関連項目:

クラスの解決とデータベースでのクラスのロードの詳細は、第11章「スキーマ・オブジェクトおよびOracle JVMユーティリティ」を参照してください。

2.4.3 クラスのロード

この項では、loadjavaユーティリティを使用してデータベースにクラスをロードする概要について説明します。loadjavaは、SQLコマンド内から実行することもできます。

ファイルからコンパイルおよびロードする従来のJVMとは異なり、Oracle JVMはデータベース・スキーマ・オブジェクトからコンパイルおよびロードします。

表2-5 Javaファイルの説明

Javaファイルの種類 説明
.javaソース・ファイルまたは.sqljソース・ファイル Javaソース・スキーマ・オブジェクトに対応
.classコンパイル済Javaファイル Javaクラス・スキーマ・オブジェクトに対応
.properties Javaリソース・ファイル、.ser SQLJプロファイル・ファイルまたはデータ・ファイル Javaリソース・スキーマ・オブジェクトに対応

すべてのクラスやリソースは、データベースにロードして、そのデータベース内の他のクラスで使用できるようにする必要があります。さらに、ロード時にはデータベース内のクラスを実行できるユーザーを定義します。

loadjavaユーティリティは、ファイルの種類ごとに次の内容を実行します。

表2-6 スキーマ・オブジェクトに対するloadjavaの操作

スキーマ・オブジェクト オブジェクトに対するloadjavaの操作
.javaソース・ファイル
  1. 別のスキーマが指定されていない場合は、定義者のスキーマにJavaソース・スキーマ・オブジェクトを作成します。
  2. スキーマ・オブジェクトにソース・ファイルの内容をロードします。

  3. ソース・ファイルに定義されているすべてのクラスに、クラス・スキーマ・オブジェクトを作成します。

  4. -resolveが指定された場合は、ソース・スキーマ・オブジェクトをコンパイルし、クラスとその依存関係を解決します。次に、コンパイル済のクラスをクラス・スキーマ・オブジェクトに格納します。

.sqljソース・ファイル
  1. 別のスキーマが指定されていない場合は、定義者のスキーマにソース・スキーマ・オブジェクトを作成します。
  2. スキーマ・オブジェクトにソース・ファイルの内容をロードします。

  3. ソース・ファイルに定義されているすべてのクラスとリソースに、クラス・スキーマ・オブジェクトを作成します。

  4. -resolveが指定された場合は、ソース・スキーマ・オブジェクトを変換およびコンパイルし、コンパイル済のクラスをクラス・スキーマ・オブジェクトに格納します。次に、プロファイルを.serリソース・スキーマ・オブジェクトに格納し、プロファイルをカスタマイズします。

.classコンパイル済Javaファイル
  1. 別のスキーマが指定されていない場合は、定義者のスキーマにクラス・スキーマ・オブジェクトを作成します。
  2. クラス・ファイルをスキーマ・オブジェクトにロードします。

  3. -resolveが指定された場合は、クラスおよびその依存関係を解決して検証します。

.properties Javaリソース・ファイル
  1. 別のスキーマが指定されていない場合は、定義者のスキーマにリソース・スキーマ・オブジェクトを作成します。
  2. リソース・ファイルをスキーマ・オブジェクトにロードします。

.ser SQLJプロファイル
  1. 別のスキーマが指定されていない場合は、定義者のスキーマにリソース・スキーマ・オブジェクトを作成します。
  2. .serリソース・ファイルをスキーマ・オブジェクトにロードし、ファイルをカスタマイズします。


dropjavaユーティリティは、loadjavaとは逆の動作を実行します。つまり、Javaファイルに対応するスキーマ・オブジェクトを削除します。loadjavaで作成したJavaスキーマ・オブジェクトは、必ずdropjavaを使用して削除します。SQLのデータ定義言語(DDL)コマンドで削除した場合、loadjavaおよびdropjavaで保持している補助データは更新されません。 dropjavaは、SQLコマンド内から実行することもできます。

ロードした後は、データベース・スキーマのUSER_OBJECTSビューにアクセスして、クラスとリソースが正しくロードされたことを確認できます。

同じクラスの二重定義

同じクラスには、2つの異なる定義を指定できません。この規則によって、次の2つの制限を受けることになります。

  • 特定のJavaの.classファイル、またはその.javaファイルのいずれかはロードできますが、両方はロードできません。

    Oracle Databaseは、クラス・ファイルまたはソース・ファイルのどちらがロードされたかを追跡します。クラスを更新する場合は、当初ロードしたファイルと同じ種類のファイルをロードする必要があります。他の種類を更新する場合は、最初にロードしたファイルを削除してから次の種類のファイルをロードする必要があります。たとえば、クラスyのソースとしてx.javaをロードした場合、x.classをロードするには、その前にx.javaを削除する必要があります。

  • 同じスキーマの2つの異なるスキーマ・オブジェクトには、同じクラスを定義できません。たとえば、x.javaでクラスyを定義し、yの定義をz.javaに移動するとします。x.javaがすでにロードされている場合、loadjavaは、yが定義されているz.javaのロードを拒否します。かわりに、次のいずれかの方法を実行する必要があります。

    • x.javaを削除し、yが定義されているz.javaをロードします。次にyが定義されていない新しいx.javaをロードします。

    • yが定義されていない新しいx.javaをロードし、次にyが定義されているz.javaをロードします。

データベース権限とJVMパーミッションの指定

クラスをロードするには、次のSQLデータベース権限が必要です。

  • スキーマにロードする場合はCREATE PROCEDUREおよびCREATE TABLE権限。

  • 別のスキーマにロードする場合はCREATE ANY PROCEDUREおよびCREATE ANY TABLE権限。

  • oracle.aurora.security.JServerPermission.loadLibraryInClass. classname

JARファイルまたはZIPファイルのロード

loadjavaユーティリティでは、.class.java.properties.sqlj.ser.jarまたは.zipファイルを使用できます。JARファイルまたはZIPファイルには、ソース・ファイル、クラス・ファイルおよびデータファイルを挿入できます。loadjavaにJARファイルまたはZIPファイルを渡すと、loadjavaはアーカイブをオープンし、そのメンバーを個別にロードします。JARまたはZIPのスキーマ・オブジェクトはありません。JARまたはZIPの内容が前回のロード時から変更されていない場合は、再ロードされません。したがって、JARファイルまたはZIPファイルのロードによるパフォーマンス低下は、ほとんどありません。 実際には、JARファイルまたはZIPファイルのロードが、loadjavaを使用する最も簡単な方法です。


注意:

前回のロード時から変更がない場合、Oracle Databaseはクラスを再ロードしません。ただし、-forceオプションを使用することで、強制的にクラスを再ロードできます。

2.4.4 実行権限の付与

すべてのクラスを自分のスキーマにロードし、自分のスキーマの外部のクラスを参照しない場合は、クラスを実行する権限がすでにあります。自分のオブジェクトが同じスキーマにロードされた他のオブジェクトをコールするために必要な権限を持っています。つまり、クラスAがクラスBを起動する能力です。クラスAにはクラスBをコールする権限が付与されている必要があります。

Javaアプリケーションを定義するクラスは、その所有者のSQLスキーマの下にある、Oracle Database内に格納されます。デフォルトでは、特定のユーザーのスキーマに常駐するクラスは、セキュリティ保護のために他のユーザーからは実行できません。loadjava -grantオプションを使用すると、自分のクラスの実行権限を他のユーザーに付与できます。自分のクラスの実行権限は特定のユーザーまたはスキーマに付与できます。ただし、スーパーユーザーのDBAロールが指定されているロールに対しては、実行権限を付与できません。クラスの実行権限の設定は、SQLのDDL文で権限を付与または取り消す際に使用する設定と同じです。

図2-3 クラスの実行権限

クラス実行権限
画像の説明


関連項目:

JVMセキュリティのパーミッションの詳細は、第10章「Oracle Database Javaアプリケーションのパフォーマンス」を参照してください。

2.4.5 現行ユーザーの制御

JavaまたはPL/SQLコードの実行中は、常に現行ユーザーが存在します。最初に存在するユーザーは、セッションを作成するユーザーです。

実行者権限と定義者権限は、SQL、PL/SQLまたはJava Database Connectivity(JDBC)の実行時に動的に使用されるSQLの概念です。現行ユーザーは、SQLの解釈を制御し、権限を判断します。たとえば、表が単純な名前で参照される場合、その表はユーザーのスキーマに属しているとみなされます。また、リソースの要求時にチェックされる権限は、現行ユーザーに付与されている権限に基づいています。

さらに、Javaストアド・プロシージャの場合、コール仕様ではPL/SQLラッパーが使用されます。したがって、コール仕様またはJavaクラス自体のいずれかに定義者権限を指定できます。いずれかが定義者権限に再定義されている場合、コールされるメソッドはJavaクラスを配置したユーザーの管理下で実行されます。

デフォルトでは、Javaストアド・プロシージャは現行ユーザーの変更なしに実行されます。つまり、定義者権限ではなく実行者権限で実行されます。実行者権限プロシージャは、特定のスキーマにバインドされていません。データベース表などのスキーマ・オブジェクトへの未修飾の参照は、定義者ではなく現行ユーザーのスキーマで解決されます。

これに対して、定義者権限プロシージャは、定義者が常駐しているスキーマにバインドされています。これらのプロシージャは定義者権限で実行され、スキーマ・オブジェクトへの未修飾の参照は定義者のスキーマ内で解決されます。

実行者権限プロシージャを使用すると、コードを再使用でき、アプリケーション・ロジックを集中化できます。特に、異なるスキーマにデータを格納するアプリケーションで有効です。このような場合には、複数のユーザーが独自のデータを1つのコードベースで管理できます。

販売分析に定義者権限プロシージャを使用する企業を考えてみます。地区ごとの販売統計を取得するには、analyzeプロシージャによって各地区のsales表にアクセスする必要があります。そのためには、各地区サイトにプロシージャが存在している必要があります。これによりメンテナンス上の問題が発生します。

問題を解決するために、この企業ではanalyzeプロシージャの実行者権限バージョンを本社にインストールしました。その結果、図2-4に示すように、すべての地区サイトで同じプロシージャを使用して、各地区のsales表を問合せできます。

図2-4 実行者権限のソリューション

実行者権限のソリューション
画像の説明

実行者権限のデフォルト動作のオーバーライドが必要になる場合もあります。本社がanalyzeプロシージャを使用して販売手数料を算出し、中央のpayroll表を更新する場合を考えます。analyzeの実行者が従業員の給料や他の機密性の高いデータが格納されているpayroll表に直接アクセスすることは問題です。この問題は、図2-5に示すように、analyzeプロシージャから定義者権限プロシージャcalcCommをコールし、このプロシージャでpayroll表を更新することで解決します。

図2-5 間接的なアクセス

間接的なアクセス
画像の説明

実行者権限のデフォルトの動作をオーバーライドするには、loadjavaにオプション-definerを指定します。このオプションは、UNIXの機能setuidに類似していますが、-definerは、プログラム全体ではなく個々のクラスに適用される点が異なります。あるいは、SQLのDDL文を実行して現行ユーザーのAUTHIDを変更できます。

定義者ごとに異なる権限を付与したり、アプリケーションに多数のクラスを指定できます。したがって、-definerオプションは慎重に使用し、クラスには必要な権限のみ設定してください。

2.4.6 Javaアップロードのチェック

USER_OBJECTSデータベース・ビューを問い合せることで、Javaソース、クラスおよびリソースを含め、独自に所有しているスキーマ・オブジェクトに関する情報を取得できます。たとえば、ロードしたソース、クラスまたはリソースがスキーマ・オブジェクトに正しく格納されているかどうかを確認できます。

表2-7に、USER_OBJECTSの主な列とその説明を示します。

表2-7 主なUSER_OBJECT列

名前 説明
OBJECT_NAME オブジェクトの名前。
OBJECT_TYPE JAVA SOURCEJAVA CLASSJAVA RESOURCEなど、オブジェクトのタイプ。
STATUS オブジェクトの状態。値は、VALIDまたはINVALIDのいずれかです。JAVA RESOURCEに対しては常にVALIDとなります。

オブジェクト名とオブジェクト・タイプ

USER_OBJECTSOBJECT_NAMEは別名です。31文字を超える完全修飾名は別名で格納されます。


関連項目:

完全修飾名と別名の詳細は、「クラスの短縮名」を参照してください。

サーバーでスキーマ・オブジェクトの別名を使用する場合は、別名や変換規則が不明であっても、サーバーのDBMS_JAVAパッケージのLONGNAME()ファンクションを使用することで、完全修飾名による問合せから別名を取得できます。

SQL> SELECT dbms_java.longname(object_name) FROM user_objects WHERE object_type='JAVA SOURCE';

この文を実行すると、Javaソース・スキーマ・オブジェクトの完全修飾名が表示されます。別名が使用されていない場合、変換は実行されません。


注意:

SQLおよびPL/SQLでは、大/小文字は区別されません。

データベースで別名に変換されたかどうかが不明であっても、DBMS_JAVAパッケージのSHORTNAME()ファンクションを使用することで、問合せ基準として完全修飾名を使用できます。

SQL*Plus> SELECT object_type FROM user_objects WHERE object_name=dbms_java.shortname('known_fullname ');

この文を実行すると、指定された完全修飾名のスキーマ・オブジェクトのOBJECT_TYPEが表示されます。ただし、完全修飾名をデータベース・キャラクタ・セットで表現できることが前提となります。

SQL> select * from javasnm;
SHORT LONGNAME
----------------------------------------------------------------------
/78e6d350_BinaryExceptionHandl sun/tools/java/BinaryExceptionHandler
/b6c774bb_ClassDeclaration sun/tools/java/ClassDeclaration
/af5a8ef3_JarVerifierStream1 sun/tools/jar/JarVerifierStream$1

状態(STATUS)

STATUSは、Javaスキーマ・オブジェクトの妥当性を示す文字列です。Javaソース・スキーマ・オブジェクトは、正常にコンパイルされるとVALIDと表示され、Javaクラス・スキーマ・オブジェクトは、正しく解決されるとVALIDと表示されます。リソースは解決されないため、Javaリソース・スキーマ・オブジェクトは常にVALIDです。

例: USER_OBJECTSへのアクセス

次のSQL*Plusスクリプトは、USER_OBJECTSビューにアクセスして、アップロードされたJavaソース、クラスおよびリソースの情報を表示します。

COL object_name format a30
COL object_type format a15
SELECT object_name, object_type, status
       FROM user_objects
       WHERE object_type IN ('JAVA SOURCE', 'JAVA CLASS', 'JAVA RESOURCE')
       ORDER BY object_type, object_name;

次の例のように、必要な場合はUSER_OBJECTSの問合せにワイルドカードを使用できます。

SELECT object_name, object_type, status
       FROM user_objects
       WHERE object_name LIKE '%Alerter';

この文は、Alerterの文字で終わるOBJECT_NAMEエントリを検索します。

2.4.7 公開

Oracle Databaseでは、データベースにロードされたJavaメソッドは、公開されるとクライアントおよびSQLからコールできるようになります。オブジェクト自体、または個々のメソッドを公開できます。SQLのデータ操作言語(DML)文またはPL/SQLで、直接あるいは間接的にトリガーでコールするJavaストアド・プロシージャを作成する場合は、クラスの個々のメソッドを公開する必要があります。コール仕様を使用してメソッドへのアクセス方法を指定します。Javaプログラムは多数のクラスの様々なメソッドで構成されます。ただし、コール仕様で通常公開されるstaticメソッドは少数です。

2.4.8 監査

Oracle Database 10g リリース2(10.2)より前のリリースでは、データベース内のJavaクラスは直接監査できません。 ただし、PL/SQLラッパーは監査できます。 通常、Javaストアド・プロシージャはすべて同じラッパーから起動されます。 したがって、Javaストアド・プロシージャはすべて、直接ではありませんが監査可能です。

他のDDL文と同様に、Oracle Database 10g リリース2(10.2)では、ソース、クラスおよびリソースのJavaスキーマ・オブジェクトを作成、変更または削除するDDL文を監査できます。 Oracle Database 10g リリース2(10.2)では、Javaアクティビティを簡単かつ直接的に監査する監査オプションが用意されています。 また、Javaソース、クラスおよびリソースの変更も監査できます。

Javaスキーマ・オブジェクトに関連するデータベース・アクティビティを、文レベルおよびオブジェクト・レベルの2種類のレベルで監査できます。 文レベルでは、特定パターンの文に関連したすべてのアクティビティを監査できます。表2-8に、Javaスキーマ・オブジェクトに関連する文監査オプションと対応するSQL文を示します。

表2-8 Javaスキーマ・オブジェクトに関連する文監査オプション

文のオプション SQL文
CREATE JAVA SOURCE CREATE JAVA SOURCE

CREATE OR REPLACE JAVA SOURCE

ALTER JAVA SOURCE ALTER JAVA SOURCE
DROP JAVA SOURCE DROP JAVA SOURCE
CREATE JAVA CLASS CREATE JAVA CLASS

CREATE OR REPLACE JAVA CLASS

ALTER JAVA CLASS ALTER JAVA CLASS
DROP JAVA CLASS DROP JAVA CLASS
CREATE JAVA RESOURCE CREATE JAVA RESOURCE

CREATE OR REPLACE JAVA RESOURCE

ALTER JAVA RESOURCE ALTER JAVA RESOURCE
DROP JAVA RESOURCE DROP JAVA RESOURCE

たとえば、ALTER JAVA SOURCEのDDL文を監査する場合は、SQLプロンプトに次の文を入力します。

AUDIT ALTER JAVA SOURCE

オブジェクト・レベルの監査では、より詳細な監査が可能です。 このレベルの監査では、特定のオブジェクトをクローズアップすることで、特定の問題を識別できます。 表2-9に、各Javaスキーマ・オブジェクトごとのオブジェクト監査オプションを示しています。 セル内のエントリXは、そのJavaスキーマ・オブジェクトについて、対応するSQLコマンドを監査できることを示します。 エントリNAは、そのJavaスキーマ・オブジェクトについて対応するSQLコマンドが適用不可であることを示します。

表2-9 Javaスキーマ・オプションに関連するオブジェクト監査オプション

オブジェクト・オプション Javaソース Javaリソース Javaクラス
ALTER X NA X
EXECUTE NA NA X
AUDIT X X X
GRANT X X X


関連項目:

  • 『Oracle Databaseセキュリティ・ガイド』

  • 『Oracle Database SQLリファレンス』


2.5 サーバー上のユーザー・インタフェース

Oracle Databaseでは、ユーザー・インタフェースの表示に関連するライブラリも含めて、コアなJavaクラス・ライブラリはすべてサーバー上にあります。しかし、ユーザー・インタフェースを作成または表示するために、コードをサーバー上で実行することは適切ではありません。Oracle JVM環境でアプリケーションを実行しているユーザーは、Oracle Databaseを実行しているサーバーの表示と入力のハードウェアとの対話や依存は不可能です。

ディスプレイ、キーボードまたはマウスをサポートしないプラットフォームへの互換性の問題に対処するために、Java 1.4ではヘッドレスAbstract Window Toolkit(AWT)サポートが採用されています。このヘッドレスAWT APIによって、publicランタイム例外クラスjava.awt.HeadlessExceptionが新たに導入されました。ネイティブな表示デバイスに依存しているAppletクラスのコンストラクタ、軽量でないすべてのコンポーネントおよびToolkitクラスやGraphicsEnvironmentクラスのメソッドの多くは、プラットフォームで表示がサポートされていない場合に、HeadlessExceptionをスローするように変更されました。Oracle Databaseでは、ユーザー・インタフェースはクライアント・アプリケーションのみでサポートされます。したがって、Oracle JVMはヘッドレス・プラットフォームであり、これらのメソッドをコールするとHeadlessExceptionがスローされます。

ヘッドレスAWTでは、基礎となるネイティブな表示デバイスまたは入力デバイスにはアクセスしないほとんどのAWT計算が許可されます。実際に、ヘッドレスAWTは非常に強力な機能であり、プログラマに対してフォント、イメージ、印刷、および色やICC操作へのアクセスを提供します。たとえば、Oracle JVMで実行中のアプリケーションは、サーバー上でイメージを物理的に表示しようとしないかぎり、そのイメージを解析、操作および出力できます。Sun社のJVMのリファレンス実装は、ヘッドレス・モードで(プロパティを-Djava.awt.headless=trueに指定して)起動し、Oracle JVMの場合と同様のヘッドレスAWT制限で実行できます。 Oracle JVMは、ヘッドレスAWTに関してJava Compatibility Kit(JCK)に完全に準拠しています。

Oracle JVMでは、サウンド・サポートと同様のアプローチを採用しています。Oracle JVMのアプリケーションは、サウンドの再生や記録の目的で、基盤となるサウンド・システムにアクセスできません。また、システム・サウンド・リソースは、リソースにアクセスしようとするメソッドのサウンドAPI仕様に一致する方法では使用できません。たとえば、基盤となるシステム・サウンド・リソースへのアクセスを試みるjavax.sound.midi.MidiSystemのメソッドでは、システムが使用できないことを示すチェック済の例外MidiUnavailableExceptionがスローされます。ただし、ヘッドレスAWTサポートと同様に、Oracle Databaseでは、ネイティブなサウンド・デバイスなしでサウンド・ファイルを操作できるAPIをサポートしています。また、Oracle JVMは、サウンドAPIを実装する際、JCKに完全に準拠します。

2.6 クラスの短縮名

Javaソース、クラスおよびリソースは、サーバーのそれぞれのスキーマ・オブジェクトに格納されます。スキーマ・オブジェクトの名前は、関連するパスやパッケージ情報を含む完全修飾名から導出されます。ドットはスラッシュに置換されます。

ただし、スキーマ・オブジェクト名は最大31文字で、すべての文字が有効であり、データベース・キャラクタ・セットの文字に変換可能であることが必要です。完全修飾名が31文字を超える場合、または無効な文字または変換できない文字が含まれている場合、Oracle Databaseは、完全修飾名を短縮名または別名に変換して、その名前をスキーマ・オブジェクト名として使用します。Oracle Databaseは、それらの名前とその変換方法を追跡します。完全修飾名が31文字以下で、無効な文字や変換できない文字が含まれていない場合は、完全修飾名がスキーマ・オブジェクト名として使用されます。

Javaクラスとメソッドの名前はSQL識別子の最大長よりも長くなることがあるため、Oracle Databaseでは、SQLアクセスのために内部的に短縮名を使用します。Oracle DatabaseのDBMS_JAVAパッケージには、短縮名に対応する元のJavaクラス名を取得するLONGNAME()ファンクションがあります。

FUNCTION longname (shortname VARCHAR2) RETURN VARCHAR2

このファンクションは、別名を使用して指定されたJavaスキーマ・オブジェクトの完全修飾名を戻します。次に、無効なクラスの完全修飾名を出力するために使用する文の例を示します。

SELECT dbms_java.longname (object_name) FROM user_objects WHERE object_type = 'JAVA CLASS' and status = 'INVALID';

また、DBMS_JAVAパッケージのSHORTNAME()ファンクションを使用することで、データベースにフルネームを指定することもできます。このファンクションは、フルネームを入力として取得し、対応する短縮名を戻します。このファンクションは、USER_OBJECTSビューの問合せによって、クラスが正しくロードされたことを検証する際に役立ちます。

FUNCTION shortname (longname VARCHAR2) RETURN VARCHAR2

2.7 Oracle DatabaseのClass.forName()

JLSでは、Class.forName()について次のように記述されています。

クラスの完全修飾名を指定すると、このメソッドはクラスを検索、ロードおよびリンクしようとします。正常に実行されると、このクラスのClassオブジェクトへの参照を戻します。正しく実行されなかった場合は、ClassNotFoundExceptionのインスタンスがスローされます。

クラスの参照は、参照側のクラスにかわって常にClassLoaderのインスタンスを介して実行されます。Java Development Kit(JDK)実装とOracle JVM実装の違いは、クラスの検索方法です。

予定にないリゾルバを使用してクラスを検索しようとすると、間違った結果となります。たとえば、スキーマXのクラスXがスキーマYのクラスYに対して、クラスZを参照するように要求する場合は、クラスXのリゾルバを使用しようとするとエラーになります。参照を実行しているのはクラスYであるため、クラスZの検索にはクラスYに対応付けられたリゾルバが使用されます。つまり、クラスが別のスキーマに入っているときに、別のクラスの異なるリゾルバを指定すると(異なるスキーマにクラスが存在する場合はデフォルトでこの状態になります)、クラスを検索できない場合があります。

このようなリゾルバの問題は、次の方法で解決できます。


注意:

システム・クラスがClass.forName()を起動すると、予期しない動作が発生することがあります。目的のクラスがSYSまたはPUBLICに常駐している場合のみ、そのクラスが検索されます。クラスがSYSまたはPUBLICのいずれにも常駐していない場合は、そのクラスに対してPUBLICシノニムを宣言できます。

この項の内容は、次のとおりです。

2.7.1 Class.forName()へのClassLoaderの指定

Oracle Databaseは、スキーマ内でのクラスの検索にリゾルバを使用します。各クラスには指定のリゾルバが対応付けられており、各クラスには異なるリゾルバを対応付けることができます。したがって、クラスの検索は対応付けられたリゾルバの定義によって決まります。ClassLoaderインスタンスは、指定されたクラスに基づいて検索に使用するリゾルバを判断します。Class.forName()ClassLoaderインスタンスを指定すると、クラスは、そのクラスのリゾルバに定義されているスキーマ内で検索されます。このClass.forName()の改良型に対する構文は次のとおりです。

Class forName (String name, boolean initialize, ClassLoader loader);

次の例は、現行のクラス・インスタンスまたはコール側のクラス・インスタンスのクラス・ローダーを指定する方法を示します。

例2-1 現行のクラスからのリゾルバの取得

Class.getClassLoader()メソッドを使用することで、任意のインスタンスのクラス・ローダーを取得できます。次の例は、インスタンスxで表されたクラスのクラス・ローダーを取得します。

Class c1 = Class.forName (x.whatClass(), true, x.getClass().getClassLoader());

例2-2 コール側クラスからのリゾルバの取得

oracle.aurora.vm.OracleRuntime.getCallerClass()メソッドを使用することで、実行中のメソッドをコールしたインスタンスのクラスを取得できます。 クラスを取得した後は、戻されたクラスのClass.getClassLoader()メソッドをコールします。次の例では、workForCaller()メソッドをコールしたインスタンスのクラスを取得します。次に、そのクラス・ローダーを取得し、Class.forName()メソッドに渡します。結果として、クラスの検索に使用されたリゾルバがコール側クラスのリゾルバとなります。

void workForCaller()
{
  ClassLoader c1=oracle.aurora.vm.OracleRuntime.getCallerClass().getClassLoader();
  ...
  Class c=Class.forName(name, true, c1);
  ...
}

2.7.2 classForNameAndSchema()へのクラス名とスキーマ名の指定

検索するスキーマを特定できるリゾルバを指定することで、クラスを検索する位置の問題を解決できます。あるいは、クラスがロードされているスキーマを指定できます。クラスのロード先スキーマが判明している場合は、classForNameAndSchema()メソッドを使用できます。このメソッドは、Oracle Databaseが提供するDbmsJavaクラスにあります。このメソッドには、クラス名とそのクラスが常駐するスキーマ名の両方を指定します。このメソッドによって、指定されたスキーマでのクラスの位置が特定されます。

例2-3 スキーマ名とクラス名の指定

次の例は、save()メソッドを使用してスキーマ名とクラス名を保存する方法を示しています。クラス名とスキーマ名が取得され、DbmsJava.classForNameAndSchemaメソッドを使用してそのクラスの位置が特定されます。

import oracle.aurora.rdbms.ClassHandle;
import oracle.aurora.rdbms.Schema;
import oracle.aurora.rdbms.DbmsJava;

void save (Class c1)
{
  ClassHandle handle = ClassHandle.lookup(c1);
  Schema schema = handle.schema();
  writeName (schema.getName());
  writeName (c1.getName());
}

Class restore()
{
  String schemaName = readName();
  String className = readName();
  return DbmsJava.classForNameAndSchema (schemaName, className);
}

2.7.3 lookupClass()へのクラス名とスキーマ名の指定

スキーマ名とクラス名が入った1つのString値をoracle.aurora.util.ClassForName.lookupClass()メソッドに指定できます。このメソッドをコールすると、指定されたスキーマでそのクラスの位置が特定されます。文字列は次の形式で指定する必要があります。

"<schema>:<class>"

たとえば、スキーマSCOTTcom.package.myclassの位置を特定するには、次のコードを使用します。

oracle.aurora.util.ClassForName.lookupClass("SCOTT:com.package.myclass");


注意:

スキーマ名には大文字を使用してください。この場合、スキーマ名は大/小文字が区別されます。

2.7.4 シリアライズでのクラス名とスキーマ名の指定

クラスのシリアライズを解除すると、処理の一部として、名前に基づいてクラスが検索されます。検索が正常に行われるためには、シリアライズされたオブジェクトにクラス名とスキーマ名の両方が含まれている必要があります。

Oracle Databaseでは、オブジェクトをシリアライズまたはシリアライズを解除する際に、次のクラスを使用できます。

  • oracle.aurora.rdbms.DbmsObjectOutputStream

    このクラスはjava.io.ObjectOutputStreamを拡張し、適切な場所にスキーマ名を追加します。

  • oracle.aurora.rdbms.DbmsObjectInputStream

    このクラスはjava.io.ObjectInputStreamを拡張し、DbmsObjectOutputStreamによって書き込まれたストリームを読み取ります。このクラスはあらゆる環境で使用できます。Oracle Databaseで使用した場合は、スキーマ名が読み取られ、クラスを検索する際に使用されます。クライアントで使用した場合、スキーマ名は無視されます。

2.7.5 Class.forNameの例

次に、クラスを参照するいくつかのメソッドの例を示します。

import oracle.aurora.vm.OracleRuntime;
import oracle.aurora.rdbms.Schema;
import oracle.aurora.rdbms.DbmsJava;

public class ForName
{
  private Class from;

  /* Supply an explicit class to the constructor */
  public ForName(Class from)
  {
    this.from = from;
  }

  /* Use the class of the code containing the "new ForName()" */
  public ForName()
  {
    from = OracleRuntime.getCallerClass();
  }

  /* lookup relative to Class supplied to constructor */
  public Class lookupWithClassLoader(String name) throws ClassNotFoundException
  {
    /* A ClassLoader uses the resolver associated with the class*/
    return Class.forName(name, true, from.getClassLoader());
  }

  /* In case the schema containing the class is known */
  static Class lookupWithSchema(String name, String schema)
  {
    Schema s = Schema.lookup(schema);
    return DbmsJava.classForNameAndSchema(name, s);
  }
}

この例では、次のメソッドを使用してクラスを検索します。

  • インスタンスのクラスのリゾルバを使用するには、lookupWithClassLoader()をコールします。このメソッドによって、from変数のClass.forName()メソッドにクラス・ローダーが指定されます。from変数に指定されたクラス・ローダーは、このクラスにデフォルト設定されます。

  • 特定のクラスのリゾルバを使用するには、明示したクラス名の後にlookupWithClassLoader()を指定してForName()をコールします。ForName()メソッドによって、from変数が指定のクラスに設定されます。lookupWithClassLoader()メソッドは指定されたクラスのクラス・ローダーを使用します。

  • コール側クラスのリゾルバを使用するには、最初に、パラメータを指定せずにForName()メソッドをコールします。このメソッドによってfrom変数がコール側クラスに設定されます。次に、lookupWithClassLoader()メソッドをコールし、コール側クラスのリゾルバを使用してクラスを検索します。

  • スキーマを指定してクラスを検索するには、lookupWithSchema()メソッドをコールします。このメソッドは、classForNameAndSchema()メソッドにクラス名とスキーマ名を渡します。

2.8 オペレーティング・システム・リソースの管理

オペレーティング・システムのリソースは、どのコンピュータでも制限されています。Javaは、プログラミング言語であると同時に、コンピューティング・プラットフォームの提供を目指しています。したがって、プラットフォームに依存しないクラス、およびプラットフォーム固有のリソースにアクセスするためのフレームワークが用意されています。 Javaのクラス・メソッドはJVMを介してオペレーティング・システム・リソースにアクセスします。Javaのこのモデルには潜在的な問題があります。この問題は、ガベージ・コレクタが管理するのはJavaオブジェクトで、そのJavaオブジェクトが保持するオペレーティング・システム・リソースではない場合に、プログラマが全リソースの管理をそのガベージ・コレクタに委任することに起因しています。

さらに、共有サーバーを使用している場合、Javaオブジェクト内に保持されているオペレーティング・システム・リソースは、1つのセッションの複数のコールにわたって保持されると無効になる可能性があります。

次の各項では、これらの問題について説明します。

2.8.1 オペレーティング・システム・リソースの概要

一般に、オペレーティング・システム・リソースには次のリソースが含まれています。

オペレーティング・システム・リソース 説明
メモリー Oracle Databaseはメモリーを内部的に管理します。オブジェクトを作成するとメモリーを割り当て、オブジェクトが不要になるとメモリーを解放します。言語とクラス・ライブラリでは、メモリーの割当ておよび解放の直接的な手段はサポートされていません。

関連項目: 「ガベージ・コレクションを使用した自動記憶域管理」

ファイルとソケット Javaにはファイル・リソースまたはソケット・リソースを表すクラスが含まれています。これらのクラスのインスタンスは、ファイル・ハンドルなど、オペレーティング・システムのファイルまたはソケットの構成メンバーを保持します。
スレッド 拡張性の問題があるため、Oracle JVMではスレッドの使用は禁止されています。ただし、データベースではマルチスレッド・アプリケーションを実行できます。

関連項目: 「Oracle Databaseのスレッド」


オペレーティング・システム・リソースへのアクセス

デフォルトでは、Javaユーザーはオペレーティング・システムの大半のリソースに直接アクセスできません。システム管理者は、JVMセキュリティ制限を変更することで、ユーザーにオペレーティング・システムのリソースにアクセスするパーミッションを付与できます。JVMセキュリティは、Java2セキュリティに準拠したシステム・リソースで施行されます。

オペレーティング・システム・リソースの存続期間

オペレーティング・システム・リソースへのアクセスには、標準のコアJavaクラスとメソッドを使用できます。リソースにアクセスした後、そのリソースがアクティブである期間は、リソースの種類によって異なります。メモリーはガベージ・コレクションされます。専用モードのサーバーを使用している場合、ファイル、スレッドおよびソケットは、複数コールにわたって存続します。共有サーバー・モードでは、ファイル、スレッドおよびソケットはコール終了時に終了します。

2.8.2 ガベージ・コレクションとオペレーティング・システム・リソース

メモリーがJavaオブジェクト・メモリーとオペレーティング・システムの構成メンバーの2つの領域に分割されているとします。Javaオブジェクト・メモリー領域にはすべてのオブジェクトと変数が含まれています。オペレーティング・システムの構成メンバーには、オペレーティング・システムが要求に応じてオブジェクトに割り当てるリソースがあります。これらのリソースにはファイルやソケットなどが含まれます。

基本的なプログラミング規則では、Javaオブジェクトとオペレーティング・システム構成メンバーの両方のメモリーをクローズするように規定されています。Javaプログラマは、ガベージ・コレクタによってメモリーが解放されると誤解することがあります。 ガベージ・コレクタは、未使用のJavaオブジェクト・メモリーを収集するために作成されました。ただし、オペレーティング・システムの構成メンバーはクローズされません。オペレーティング・システムの構成メンバーは、Javaオブジェクトをガベージ・コレクションする前にプログラムでクローズする必要があります。

たとえば、オブジェクトがファイルをオープンするたびに、オペレーティング・システムはそのファイルを作成してオブジェクトにファイル・ハンドルを割り当てます。 ファイルがクローズされないと、オペレーティング・システムはコールの終了またはJVMの終了までファイル・ハンドルの構成メンバーを保持します。そのため、これらの構成メンバーが不足する可能性があります。各オペレーティング・システムのハンドルは無限ではありません。ハンドルの不足を回避するには、メソッドを終了する前にリソースをクローズします。ソケットに連結されたストリームについてもソケットをクローズする前にクローズします。

パフォーマンス上の理由から、ガベージ・コレクタは各オブジェクトにハンドルがあるかどうかを確認できません。したがって、ガベージ・コレクタはJavaオブジェクトおよび変数を収集しますが、ハンドルを解放するための適切なオペレーティング・システム・メソッドは発行しません。

例2-4に、オペレーティング・システムの構成メンバーのクローズ方法を示します。

例2-4 オペレーティング・システム・リソースのクローズ

public static void addFile(String[] newFile)
{
  File inFile = new File(newFile);
  FileReader in = new FileReader(inFile);
  int i;

  while ((i = in.read()) != -1)
    out.write(i);

  /*closing the file, which frees up the operating system file handle*/
  in.close();
}

inFileをクローズしない場合、Fileオブジェクトは最終的にはガベージ・コレクションされます。Fileオブジェクトがガベージ・コレクションされても、ファイルがクローズされていないため、オペレーティング・システムでは、そのファイルが使用中と判断されます。


注意:

リソースをクローズするために、Javaファイナライザを使用することが必要な場合もあります。 ただし、ファイナライザではタイムリな実行が保証されません。また、ガベージ・コレクタに時間的な余裕があると、ファイナライザは実行するためにキューに挿入されます。ファイナライザ内のリソースをクローズすると、JVMが終了するまでリソースが解放されない可能性があります。メソッドの中でリソースをクローズすることが最も確実な方法です。

2.9 Oracle Databaseのスレッド

Oracle JVMは、非プリエンプティブ・スレッド・モデルを実装しています。このモデルでは、JVMは1つのオペレーティング・システム・スレッドですべてのJavaスレッドを実行します。JVMはラウンドロビン法でスレッドをスケジューリングし、ブロックされた場合にのみスレッドを切り替えます。たとえば、Thread.yield()メソッドをコールする場合、またはmySocket.read()をコールしてネットワーク・ソケットを待機する場合に、ブロックが発生します。

次の表に、Oracle Databaseのスレッド・モデルに関する利点と不利な点を示します。

利点 不利な点
  • プログラムが容易
  • スレッドの切替えにシステム・コールが不要なため、JVMの実装が効率的

  • JVMはプリエンプティブJVMを中断し、ランタイム例外が発生するデッドロックを検出できるため、より安全

  • 同時実行性がない
  • 移植性がない

  • スレッドをブロックしている場合はロックするためにシステム・コールが必要で、パフォーマンス低下の恐れがある

  • 効率的なマルチスレッド・メモリーの割当てに大量のメモリー・プールが必要なため、メモリーの拡張性が低下する


このモデルが採用された理由は、単一プロセッサ・システムで作成されたJavaアプリケーションが、マルチプロセッサ・システムで作成された場合とまったく同様に動作するためです。また、Oracle JVMはデータベースに埋め込まれているため、従来のJVMよりも高い同時実行性が提供され、Javaスレッド間での同時実行性がないことも問題になりません。

OracleサーバーはプリエンプティブにJVMセッションをスケジューリングするため、アプリケーション・ロジックの中でスレッドを使用する必要がありません。同時に数百または数千のトランザクションをサポートする必要がある場合は、対応する各JVMでトランザクションを開始します。これは、Oracle JVMでセッションを作成する場合とまったく同じです。JVM間の調整とデータ転送は、Oracle Databaseの通常のトランザクション機能で実行されます。一般的なJVMのフットプリントは6〜8MBであるのに対して、Oracle Databaseでは個々に使用するメモリーが40KB未満のJVMを何千も作成できるため、拡張性の問題もありません。

スレッドは、1つのスレッドの提供が完了またはブロックされるまで、Oracle JVM内で管理されます。ネットワーク・ソケットを生成または待機することでスレッドがブロックされると、JVMは別のスレッドを提供します。ただし、スレッドがブロックされない場合は、完了するまでそのスレッドが提供されます。

Oracle JVMには、パフォーマンスとスレッド管理を向上させるために、次の機能が追加されました。

スレッドのライフ・サイクル

シングル・スレッド・アプリケーションでは、次のイベントの発生でコールが終了します。

初期スレッドが他のJavaスレッドを作成して起動した場合は、次のいずれかの方法でコールが終了します。

共有サーバー・モードでは、戻し操作または捕捉操作の例外が原因でコールが終了すると、Oracle JVMは、すべてのデーモン・スレッドでThreadDeathExceptionのインスタンスをスローします。このThreadDeathExceptionは、原則的にスレッドの実行を停止します。

専用サーバー・モードおよび共有サーバー・モードでは、System.exit()またはoracle.aurora.vm.OracleRuntime.exitCall()のコールが原因でコールが終了すると、Oracle JVMは、そのコールをただちに終了し、すべてのスレッドを終了しますが、ThreadDeathExceptionはスローしません。

コール時に、Javaプログラムが再帰的に追加のJavaコードを実行することがあります。たとえば、JDBCまたはSQLJを使用してプログラムでSQL問合せを発行すると、Javaで作成されたトリガーがコールされることがあります。コールの存続期間に関するこれまでの説明は、Javaコードへの最上位コールに関するもので、再帰的コールに関する説明ではありません。たとえば、再帰的コール内からSystem.exit()をコールすると、再帰的コールのみではなく、Javaへの最上位コール全体が終了します。

System.exit()、OracleRuntime.exitSession()およびOracleRuntime.exitCall()

System.exit()メソッドをコールすると、Javaの状態が保持されずにJVMが終了します。 これによってデータベース・セッションが終了したりクライアントが切断されることはありません。 ただし、その直後にデータベース・セッション自体が終了することが頻発します。 また、OracleRuntime.exitSession()でも、Javaの状態が保持されずにJVMが終了します。 ただし、このメソッドではデータベース・セッションも終了し、クライアントが切断されます。

OracleRuntime.exitCall()の動作は、OracleRuntime.threadTerminationPolicy()によって異なります。 このメソッドでは、boolean値が戻されます。 これがtrueの場合、データベース・コールの終了時に、アクティブなスレッドが休止状態ではなく終了します。 共有サーバー・プロセスでは、threadTerminationPolicy()は常にtrueです。 シャドウ(専用)プロセスでは、デフォルト値はfalseです。 この値を変更するには、OracleRuntime.setThreadTerminationPolicy()をコールします。

また、OracleRuntime.callExitPolicy()という別のメソッドがあります。 このメソッドでは、OracleRuntime.exitSession()OracleRuntime.exitCall()またはSystem.exit()メソッドがそれまでにコールされていない場合に、コールを終了するタイミングが決定されます。 コール終了ポリシーは、OracleRuntime.setCallExitPolicy()を使用して、次のいずれかの値に設定できます。

Oracle9i Databaseでは、JVMはcallExitPolicy()OracleRuntime.EXIT_CALL_WHEN_ALL_NON_DAEMON_THREADS_TERMINATEであり、共有および専用サーバー・プロセスの両方でthreadTerminationPolicy()trueであるかのように動作します。 これは、この時点でデーモン・スレッドが停止することを意味します。 また、exitCall()が実行された場合、共有および専用サーバー・プロセスの両方で、コールの終了前にすべてのスレッドが停止します。

すべてのリリースにおいて、System.exit()およびOracleRuntime.exitSesssion()のどちらでもJVMがただちに終了しますが、どのスレッドでもfinallyブロックは実行されません。 また、OracleRuntime.exitCall()は、各スレッドに対してThreadDeath例外をスローして各スレッドの終了を試みます。これにより、アクティブなスレッド・スタックのfinallyブロックが実行されます。

2.10 共有サーバーに関する考慮事項

共有サーバーを使用するセッションでは、複数のコールにわたる制限が存在します。これは、共有サーバーを使用するセッションは、後続のデータベース・コールによる同一プロセスへの接続が保証されていないためです。したがって、複数コールにわたって保持する必要があるセッション固有のメモリーおよびオブジェクトは、SGAに保存されます。 つまり、スレッド、オープン・ファイルおよびソケットなどのプロセス固有のリソースは、各コールの終了時にクリーン・アップする必要があるため、次のコールでは使用できません。

この項の内容は、次のとおりです。

2.10.1 コール終了時の移行

共有サーバー・モードの場合、Oracle Databaseでは、Javaプログラムの状態を次のコールまで保持するために、コール終了時にstatic変数から参照可能なすべてのオブジェクトがセッション・スペースに移行されます。セッション・スペースはクライアントのセッション内にあり、複数コールにわたって存続するstatic変数およびオブジェクトが格納されます。この移行処理は、各コールの終了時にOracle JVMで実行されます。

この移行処理にはメモリーが使用され、パフォーマンスにも影響します。したがって、複数のコールにわたって保持するstatic変数やオブジェクトは最小限に抑えるように注意してください。static変数にオブジェクトを不要に格納すると、メモリー・マネージャが移行処理を実行し、セッションごとにリソースを使用することになるため、不要な負担がかかります。static変数を必要最低限に抑えることで、メモリー・マネージャの負担を軽減し、サーバーのパフォーマンスを向上させることができます。

Javaプログラムを同時に実行できるユーザー数を最大にするには、セッションのフットプリントを最小にすることが重要です。特に、拡張性を最大にするには、非アクティブなセッションが占有するメモリー領域を必要最小限にする必要があります。フットプリントを最小化する簡単な方法は、コール終了ごとに大きなデータ構造を解放することです。多くのデータ構造は、別のコールで再度必要になったときに遅延して再作成できます。このような理由から、Oracle JVMには、コール終了時など、セッションが非アクティブになる直前に、指定のJavaメソッドをコールするためのメカニズムがあります。

このメカニズムは、EndOfCallRegistry通知と呼ばれます。このメカニズムによって、コール終了時にstatic変数を消去し、次のコールの着信時に遅廷初期化テクニックを使用して変数を再初期化できます。この方法は、複数コール間で静的変数を保持するためにメモリー・マネージャが必要としている記憶域の容量に懸念がある場合のみ実行してください。複雑でステートフルなサーバー・アプリケーションをJavaで実装する場合に考慮する必要があります。

コール終了時にデータ構造をNULLにリセットし、その後新しいコールごとにデータ構造を再作成するかどうかの決定は、時間と領域のトレードオフの問題です。データ構造の再作成によって処理時間は多少長くなりますが、次のコールまでデータ構造を保持する必要がないため、メモリー領域をかなり節約できます。また、セッション領域に移行されたオブジェクト(特に大型のオブジェクト)のアクセスには時間がかかり、効率が悪くなります。これは、セッションの表現がコール領域に基づくオブジェクトとは異なることに起因しています。

このような最適化の対象として、次のデータ構造があります。

  • バッファまたはキャッシュ。

  • 静的フィールド(配列など)。これは、一度初期化するとプログラムの実行が完了するまで変化しません。

  • 動的に作成されるデータ構造。最適化によって、コール間のメモリー使用効率およびコール中の速度効率の向上が期待できます。このデータ構造の最適化によってコードが複雑になり、管理が困難になることもあります。したがって、労力に見合う程度の領域が節約される場合のみ、この最適化の実施を検討してください。

2.10.2 コール終了時の最適化に対するOracle固有のサポート

コール終了時に消去するstatic変数は、バッファ、フィールドまたはデータ構造の作成時に登録できます。oracle.aurora.memoryManager.EndOfCallRegistryクラスでは、registerCallback()メソッドによって、Callbackオブジェクトを実装するオブジェクトが取得されます。registerCallback()メソッドには、このオブジェクトがコール終了時まで格納されます。コール終了時に、Oracle JVMは登録されたすべてのCallbackオブジェクトのact()メソッドをコールします。Callbackオブジェクト内のact()メソッドは、ユーザー定義のバッファ、フィールドまたはデータ構造を消去するために実装されます。消去されると、Callbackオブジェクトはレジストリから削除されます。


注意:

コール終了時がセッションの終了時でもある場合は、いずれにしてもセッション・スペースが消去されるため、コールバックは開始されません。

弱い表では、エンドオブコール・コールバックのレジストリが保持されます。Callbackオブジェクトまたは値のいずれかがJavaプログラムから参照できない場合は、オブジェクトと値の両方が表から削除されます。弱い表を使用してコールバックを保持すると、コールバックを登録しても、ガベージ・コレクタによるオブジェクトの削除は回避されません。したがって、必要なコールバックを自分で保持する必要があり、表に保持しておくことはできません。

EndOfCallRegistryの使用方法は、操作対象のオブジェクトがstaticフィールドに保持されているか、インスタンス・フィールドに保持されているかによって異なります。

静的フィールド

EndOfCallRegistryを使用して、クラス全体に対応付けられた状態を消去します。この場合、Callbackオブジェクトはprivate staticフィールドに保持する必要があります。コール間で削除されたキャッシュ内のデータにアクセスする必要があるコードは、キャッシュ・データを遅延して作成または再作成するメソッドをコールする必要があります。

次の例を考えてみます。

import oracle.aurora.memoryManager.Callback;
import oracle.aurora.memoryManager.EndOfCallRegistry;

class Example
{
  static Object cachedField = null;
  private static Callback thunk = null;

  static void clearCachedField()
  {
    // clear out both the cached field, and the thunk so they don't
    // take up session space between calls
    cachedField = null;
    thunk = null;
  }

  private static Object getCachedField()
  {
    if (cachedField == null)
    {
      // save thunk in static field so it doesn't get reclaimed
      // by garbage collector
      thunk = new Callback () {
        public void act(Object obj)
        {
          Example.clearCachedField();
        }
      };

      // register thunk to clear cachedField at end-of-call.
      EndOfCallRegistry.registerCallback(thunk);
      // finally, set cached field
      cachedField = createCachedField();
    }
    return cachedField;
  }

  private static Object createCachedField()
  {
    ...
  }
}

この例では、次の手順が実行されます。

  1. staticフィールドthunkCallbackオブジェクトを作成します。

  2. このCallbackオブジェクトをコール終了時の移行に登録します。

  3. Callback.act()メソッドを実装して、Callbackオブジェクト自体も含めて、すべてのstatic変数を解放します。

  4. キャッシュを遅延して再作成するためのcreateCachedField()メソッドを指定します。

ユーザーがキャッシュを作成すると、CallbackオブジェクトがgetCachedField()メソッドに自動的に登録されます。コール終了時に、Oracle JVMは、登録されたCallback.act()メソッドをコールして、静的メモリーを解放します。

インスタンス・フィールド

インスタンス・フィールドに保持されているデータ構造を消去状態にするには、EndOfCallRegistryを使用します。たとえば、状態があるクラスの各インスタンスに対応付けられている場合、各インスタンスにはそのインスタンスのキャッシュ状態を保持するフィールドがあり、必要に応じてキャッシュ・フィールドが埋められます。状態が確実にキャッシュされるメソッドを使用してキャッシュ・フィールドにアクセスできます。

次の例を考えてみます。

import oracle.aurora.memoryManager.Callback;
import oracle.aurora.memoryManager.EndOfCallRegistry;

class Example2 implements Callback
{
  private Object cachedField = null;

  public voidact (Object obj)
  {
    // clear cached field
    cachedField = null;
    obj = null;
  }

  // our accessor method
  private static Object getCachedField()
  {
    if (cachedField == null)
    {
      // if cachedField is not filled in then we need to
      // register self, and fill it in.
      EndOfCallRegistry.registerCallback(self);
      cachedField = createCachedField();
    }
    return cachedField;
  }

  private Object createCachedField()
  {
    ...
  }
}

この例では、次の手順が実行されます。

  1. インスタンスをCallbackオブジェクトとして実装します。

  2. Callback.act()メソッドを実装して、インスタンス・フィールドを解放します。

  3. ユーザーがキャッシュを要求すると、Callbackオブジェクトによって、このオブジェクト自体がコール終了時の移行に登録されます。

  4. キャッシュを遅延して再作成するためのcreateCachedField()メソッドを指定します。

ユーザーがキャッシュを作成すると、CallbackオブジェクトがgetCachedField()メソッドに自動的に登録されます。コール終了時に、Oracle JVMは、登録されたCallback.act()メソッドをコールして、キャッシュを解放します。

この方法を使用すると、Callbackオブジェクトとそのインスタンスが同じオブジェクトになるため、オブジェクトの存続期間とそのインスタンスの存続期間が等しくなります。

2.10.3 EndOfCallRegistry.registerCallback()メソッド

registerCallback()メソッドは、Callbackオブジェクトをレジストリにインストールします。コール終了時に、Oracle JVMは登録されたすべてのCallbackオブジェクトのact()メソッドをコールします。

Callbackオブジェクトは、自動的に、またはObjectインスタンスを使用して登録できます。オブジェクトに格納されたその他の情報をact()に渡す必要がある場合は、このオブジェクトをvalueパラメータに登録できます。このパラメータは、Objectのインスタンスです。

registerCallback()メソッドの有効なシグネチャは、次のとおりです。

public static void registerCallback(Callback thunk, Object value);

public static void registerCallback(Callback thunk);

次の表に、registerCallbackのパラメータとその説明を示します。

パラメータ 説明
thunk コール終了時の移行でコールされるCallbackオブジェクトを示します。
value オブジェクトに格納されたその他の情報をact()に渡す必要がある場合は、このオブジェクトをvalueパラメータに登録できます。場合によっては、コールバックに必要な状態の保持にvalueパラメータが必要になります。ただし、大半のユーザーは、このパラメータの値を指定する必要はありません。

2.10.4 EndOfCallRegistry.runCallbacks()メソッド

runCallbacks()メソッドのシグネチャは、次のとおりです。

static void runCallbacks()

JVMはコール終了時に、このメソッドをコールし、registerCallback()を使用して登録したすべてのCallbackオブジェクトのact()をコールします。コール終了時にこのメソッドがコールされた後で、オブジェクトが移行され、最終処理が行われます。


注意:

このメソッドは、作成したコード内ではコールしないでください。

2.10.5 Callbackインタフェース

インタフェースの宣言は、次のとおりです。

Interface oracle.aurora.memoryManager.Callback

EndOfCallRegistry.registerCallback()を使用して登録するオブジェクトは、Callbackインタフェースを実装する必要があります。このインタフェースは、アプリケーションで、コール終了時に通知が必要な場合に役立ちます。

2.10.6 Callback.act()メソッド

act()メソッドのシグネチャは、次のとおりです。

public void act(Object value)

コールの終了時に実行する必要がある任意のアクティビティを実装できます。通常、このメソッドにはセッション・スペースに保存されるメモリーを消去するプロシージャが含まれます。

2.10.7 複数のコールに影響を与えるオペレーティング・システム・リソース

次の表に示すとおり、共有サーバー・モードでは、データベース・コールの終了時にOracle JVMが、オープン状態のオペレーティング・システム・リソースをすべてクローズします。

リソース 存続期間
ファイル データベースのコールが終了すると、オープン状態のすべてのファイルがクローズされます。
スレッド コールが終了すると、すべてのスレッドが終了します。
ソケット
  • クライアント・ソケットは複数コールにわたって存続できます。
  • サーバー・ソケットは、コールが終了すると終了します。

オペレーティング・システム・リソースに依存するオブジェクト Javaオブジェクトの有効期間は、そのオブジェクトの使用可能期間に関係なく、セッションが存続している期間になります。たとえば、staticクラス変数あるいはこのクラスを直接または間接的に参照するクラス変数にJavaオブジェクトを格納すると、セッション終了まで有効になります。使用可能期間の終了後にJavaオブジェクトを使用しようとすると、Oracle Databaseから例外がスローされます。これは次のような例に当てはまります。
  • 前のコールの終了時にクローズしたjava.io.FileInputStreamから読み取ろうとすると、java.io.IOExceptionが発生します。

  • 以前のコールで実行したThreadオブジェクトが後続のコールでもアクセス可能な場合は、java.lang.Thread.isAlive()falseになります。


1つのコールの終了時には、そのコールに対してローカルなリソースをクローズしてください。ただし、オペレーティング・システム・リソースを保持するstaticオブジェクトについては、これらのリソースがコール終了後に受ける影響を考慮する必要があります。

ファイル

共有サーバー・モードでは、コールが終了すると、Oracle JVMはオープン状態のオペレーティング・システム構成メンバーを自動的にクローズします。これは、Javaオブジェクト内のオペレーティング・システム・リソースすべてに影響を与えます。static変数の中でファイルをオープンした場合、そのファイル・ハンドルはコールの終了時にクローズされます。したがって、複数のコールにまたがってFileオブジェクトを保持すると、次にファイル・ハンドルを使用するときに例外がスローされます。

例2-5Concatクラスは、複数のファイルを1つのファイルoutFileに書き込むことができます。最初のコールでoutFileが作成されます。最初の入力ファイルがオープンされ、読み取られてoutFileに記録され、コールが終了します。outFilestatic変数として定義されているため、次のコールが起動される間にセッション・スペースに移動します。ただし、ファイル・ハンドルは、コールが終了するとクローズします。次回addFile()をコールすると、例外が発生します。

例2-5 オペレーティング・システム・リソースの調整

public class Concat
{
  static File outFile = new File("outme.txt");
  FileWriter out = new FileWriter(outFile);

  public static void addFile(String[] newFile)
  {
    File inFile = new File(newFile);
    FileReader in = new FileReader(inFile);
    int i;

    while ((i = in.read()) != -1)
      out.write(i);
    in.close();
  }
}

これには対処法があります。コールが終了するたびにファイルやバッファなどをクローズし、次のコールの開始時にリソースを再度オープンして、ハンドルを有効な状態に維持します。もう1つの方法は、オペレーティング・システム・リソースではなくデータベースを使用することです。たとえば、ファイルではなくデータベース表を使用します。あるいは、複数のコールにわたって存続するstaticオブジェクトにオペレーティング・システム・リソースを格納しないようにします。かわりに、そのコールに対するローカルなオブジェクトの中でのみ、オペレーティング・システム・リソースを使用します。

例2-6に、例2-5の場合のようにオペレーティング・システム・リソースを調整せずに連結する方法を示します。addFile()メソッドは、各コールでoutme.txtファイルをオープンし、そのファイルに書き込まれた内容がすべて最後に追加されていることを確認します。このファイルは、各コールの終了時にクローズします。この場合、次の2つの状態が発生します。

  • Fileオブジェクトはコール外では存続しなくなります。

  • オペレーティング・システム・リソースであるoutme.txtファイルはコールごとに再度オープンされます。Fileオブジェクトをstatic変数に指定した場合は、各コール内でoutme.txtをクローズすることで、オペレーティング・システム・リソースを調整する必要はなくなります。

例2-6 オペレーティング・システム・リソースの正しい管理

public class Concat
{

  public static void addFile(String[] newFile)
  {
    /*open the output file each call; make sure the input*/
    /*file is written out to the end by making it "append=true"*/
    FileWriter out = new FileWriter("outme.txt", TRUE);
    File inFile = new File(newFile);
    FileReader in = new FileReader(inFile);
    int i;

    while ((i = in.read()) != -1)
      out.write(i);
    in.close();

    /*close the output file between calls*/
    out.close();
  }
}

ソケット

ソケットは、クライアントとサーバー間の接続をセットアップするために使用されます。データベース接続ごとに、接続の片側でソケットが使用されます。接続のセットアップはアプリケーションでは行われません。接続は、基礎となるプロトコル(Oracle NetのTTCまたはIIOP)で実行されます。


関連項目:

接続の構成方法については、「OracleJVMの構成」を参照してください。

データベースに格納された1つのクラス内から指定のURLに接続するなど、別の接続をセットアップすることもできます。このセットアップでは、次のコンストラクタを使用して接続のクライアント側とサーバー側に対応するようにソケットをインスタンス化します。

  • java.net.Socket()コンストラクタで、クライアント・ソケットを作成します。

  • java.net.ServerSocket()コンストラクタで、サーバー・ソケットを作成します。

ソケットは接続の両側にあります。着信コールをリスニングするサーバー側の接続に対応するのはServerSocketインスタンスです。要求を送るクライアント側の接続に対応するのはSocketインスタンスです。JVMに定義されたソケットは、共有サーバー内のServerSocketインスタンスは複数のコールにまたがって存続できないという制限の範囲内で使用できます。

次の表に、ソケットの種類とその説明を示します。

ソケットの種類 説明
Socket 接続のクライアント側は送信側になるため、Socketインスタンスは共有サーバー内の複数のコールにわたって対応できます。
ServerSocket 接続のサーバー側のリスナーです。ServerSocketインスタンスは、共有サーバー内の1つのコールが終了するとクローズします。共有サーバーはコールの終了時に別のクライアントに移動します。作成元以外のコールでServerSocketインスタンスを使用すると、ソケットがクローズしていることを示すI/O例外が発生します。

スレッド

共有サーバー・モードでは、戻し操作または捕捉操作の例外が原因でコールが終了すると、Oracle JVMは、すべてのデーモン・スレッドでThreadDeathExceptionをスローします。このThreadDeathExceptionは、原則的にスレッドの実行を停止します。複数のコールにわたって存続するスレッドに依存しているコードは、共有サーバー・モードでは予測どおりには動作しません。たとえば、データベース・コールの終了時にはすべてのスレッドが停止するため、スレッドの初期化を追跡するstatic変数の値は、後続のコールでは正確さに欠ける可能性があります。

具体的な例として、Sun社が提供するRMIサーバーは、共有サーバー・モードで機能します。ただし、1つのコールのコンテキスト内のみで有効です。これは、共有サーバー・モードではコール終了時、つまり、すべての非デーモン・スレッドが戻ったときに停止するデーモン・スレッドが、RMIサーバーによってフォーク処理されるためです。後続のコールでRMIサーバー・セッションを再び開始しても、これらのデーモン・スレッドは再開されないため、RMIサーバーは適切に機能しません。