ヘッダーをスキップ
Oracle® Database Java開発者ガイド
12cリリース1 (12.1)
B72466-05
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

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

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

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

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

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

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

図2-1の説明が続きます。
「図2-1 各データベース・セッション内のJava環境」の説明

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

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

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

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

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

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

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

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

  • Javaのstaticフィールドが参照するすべてのオブジェクト、これらのオブジェクトが参照するすべてのオブジェクトなど(オブジェクトの推移閉包まで)

  • セッションを作成したクライアントのガベージ・コレクション

  • staticフィールドのセッション・メモリーおよびコール間での必要なメモリー

  • 1つのコール内に存在するフィールドのコール・メモリー

Javaアプリケーションの実行制御

Java 2 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()が起動されます。

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

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

  • ソース・コード・ファイルは、.javaファイルとして保存されます。

  • コンパイルされたJavaバイナリ・ファイルは、.classファイルとして保存されます。

  • リソースは、.propertiesファイルや.serファイルなどの任意のデータファイルでファイルシステム階層に格納され、実行時にロードまたは使用されます。

また、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クラス

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

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

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

図2-2の説明が続きます
「図2-2 Oracle DatabaseへのJavaのロード」の説明

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

oracle.aurora.rdbms.Handle

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

oracle/aurora/rdbms/Handle

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

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

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

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

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

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

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

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

Javaクラスのコンパイル

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

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

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

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


注意:

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

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

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

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

loadjavaツールを使用したソースのコンパイル

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • online=true

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

  • debug=true

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

    javac -g
    

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

loadjavaツールにencodingコンパイラ・オプションを指定すると、.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に、これらのメソッドのパラメータを示します。

表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

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


自動再コンパイル

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ツールには、依存関係管理機能を利用しない場合に、コンパイルと解決を強制的に実行する機能もあります。

クラス依存関係の解決

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

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

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


注意:

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

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

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

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

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

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

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

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

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

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

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

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

リゾルバ仕様には、存在しないクラスへの未解決の参照を可能にする特別なオプションを指定できます。製品では内部クラスがまったく使用されないこともあります。標準のJava環境では、JVMはメソッドがコールされないかぎりメソッドを無視するため、問題は起こりません。ただし、クラスを解決するとき、Oracle JVMは使用できない名前も含め、そのクラスが参照しているすべての名前を解決しようとします。そのクラスが参照しているそれぞれの名前に対して一致するクラスをOracle JVMが見つけることができない場合、解決中のクラスは無効とマークされ、実行できません。

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

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

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

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

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

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

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

マイナス記号(-)ワイルドカードを使用する場合でも、クラスのスーパー・クラスは存在する可能性ガあります。スーパー・クラスが見つからない場合、リゾルバでマイナス記号(-)ワイルドカードを使用しているかどうかに関係なく、そのクラスは無効になります。


注意:

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

バイトコード検証機能

JVMの仕様では、定義したクラスをJVMで使用するには、その前に.classファイルを検証する必要があります。Oracle JVMでは、クラスの解決時に検証が実行されます。

表2-4に、リゾルバで検出される問題と、発行される該当のOracleエラー・コードを示します。

表2-4 ORAエラー

エラー・コード 説明

ORA-29545

リゾルバでクラスの形式が不正と判断されると、そのクラスは有効とマークされません。リゾルバでクラスが拒否されると、ORA-29545エラーが発行されます。このエラーはloadjavaツールによってレポートされます。たとえば、.classファイルの内容がJavaコンパイルの結果に一致しない場合、またはファイルが破損している場合は、このエラーがスローされます。

マイナス記号(-)ワイルドカード式をリゾルバで使用し、一部のクラスの有効性を検証していない場合も、ORA-29545エラーが発生することがあります。

ORA-29552

状況によっては実行時に例外をスローできるように、リゾルバでは、有効とマークしたクラスのバイトコードが変更されることがあります。このような場合、リゾルバはORA-29552警告を発行し、loadjavaツールがこのエラーをレポートします。Java言語仕様(JLS)でIncompatibleClassChangeErrorがスローされるように規定されている場合は、loadjavaツールがこの警告を発行します。Oracle JVMは、リゾルバにこのような状況を検出させることで、JLSで規定されているランタイム動作をサポートします。


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

B myMethod(A a)
{ 
  return a; 
}

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

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


関連項目:

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

Oracle JVMでのロギング

Oracle JVMは、ロギング・プロパティ参照の領域におけるJDK JavaロギングAPIを拡張することで、ロギング構成管理のセキュリティを強化し、ユーザー・ベースのロギング構成をサポートしています。


関連項目:

JavaロギングAPIの詳細は、次のサイトを参照してください。

http://docs.oracle.com/javase/7/docs/


セッション内でLogManagerをアクティブ化して、Oracle JVMのロギング・プロパティを初期化する必要があります。ロギング・プロパティは、データベース常駐リソース参照で拡張されたLogManager APIを使用して、各セッションで1回初期化されます。

Oracle JVMでは、次の手順を実行してロギング・オプションを構成します。

  1. java.util.logging.config.classプロパティが設定されている場合、ロギングの動作は標準JDKでの動作と同じです。

  2. java.util.logging.config.classプロパティが設定されていない場合、Oracle JVMでは現行ユーザー・スキーマのjavavm/lib/logging.propertiesリソースの可用性が検証されます。

    使用可能な場合、このリソースがLogManagerの構成設定として使用され、java.util.logging.config.fileプロパティが設定されます。

  3. 前述の条件の両方には該当しない場合、java.util.logging.config.fileプロパティが検証され、これが指定されていれば、LogManager APIで記述されているとおりに使用されます。

  4. 手順12および3のいずれの条件にも該当しない場合は、SYSスキーマのjavavm/lib/logging.propertiesリソースが使用されます。このリソースは、データベースの作成時にSYSスキーマにロードされた$(java.home)/lib/logging.propertiesファイルのコピーです。つまり、デフォルトでLogManagerは、$(java.home)/javavm/lib/logging.propertiesファイルで構成されている場合と同様に動作します。ただし、このファイルを変更しても、データベースが再作成されるまで変更は有効になりません。

javavm/lib/logging.propertiesファイル内のデフォルト設定を変更する必要がある場合は、別のプロパティ・セットを準備し、loadjavaコマンドを使用してそれらをスキーマにロードします。たとえば、スキーマがHRであり、現行ファイル・ディレクトリがmydirの場合、mydirの下にディレクトリjavavm/lib/を作成し、mydir/javavm/lib/ディレクトリの下のlogging.propertiesファイル内に必要なプロパティを指定します。続いて、次のようにしてmydirからloadjavaコマンドを起動します。

mydir% loadjava -u HR -v -r javavm/lib/logging.properties
password:<password>

loadjavaコマンドを起動した後は、mydir/javavm/lib/logging.propertiesファイルを削除できます。HRとして実行され、LogManagerのアクティブ化を実行しているセッションには、HRに専用のこのデータベース常駐リソースからのプロパティでLogManagerが構成されます。


注意:

Oracle JVMは常に、セキュリティ・マネージャを使用して実行されます。したがって、使用されているロギング構成方法に関係なく、HRにロギング許可を付与する必要があります。ほとんどの場合、権限のあるユーザーが次のコールを発行すれば、これらの許可を付与できます。
call dbms_java.grant_permission( 'HR',  'SYS:java.util.logging.LoggingPermission', 'control', '' );

クラスのロード

この項では、loadjavaツールを使用してデータベースにクラスをロードする概要について説明します。loadjavaツールを使用すると、ファイルからスキーマ・オブジェクトを生成して別のスキーマにロードできます。次に例を示します。

loadjava -u HR -schema TEST MyClass.java
Password: password

注意:

コマンドライン・インタフェースを使用する場合、クラスをスキーマ・オブジェクトとしてデータベースにロードする必要はありません。次に例を示します。
C:\oraclehome\bin>loadjava -u HR MyClass.java
Password: password

コマンドライン・インタフェースの詳細は、「コマンドライン・インタフェースの使用」を参照してください。


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

表2-5に、従来のJVMで使用されるファイルに対応するデータベース・スキーマ・オブジェクトを示します。

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

Javaファイルの種類 説明

.javaソース・ファイルまたは.sqljソース・ファイル

Javaソース・スキーマ・オブジェクトに対応

.classコンパイル済Javaファイル

Javaクラス・スキーマ・オブジェクトに対応

.properties Javaリソース・ファイル、.ser SQLJプロファイル・ファイルまたはデータファイル

Javaリソース・スキーマ・オブジェクトに対応


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

表2-6に、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ツールを使用して削除します。次に例を示します。

dropjava -u HR -schema TEST MyClass.java
Password: password

SQLのデータ定義言語(DDL)コマンドで削除した場合、loadjavaツールおよびdropjavaツールで保持している補助データは更新されません。dropjavaツールは、SQLコマンド内から実行することもできます。

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

ユーザー・クラスロード・クラスのメタデータの共有

データベース常駐クラスをロードするための組込みメカニズムによってロードされたクラスはシステム・クラスロードと呼ばれ、その他の方法によってロードされたクラスはユーザー・クラスロードと呼ばれます。データベースにクラスをロードする場合、クラスの表現はメモリーで作成され、その一部はクラス・メタデータと呼ばれます。クラス・メタデータはクラスを使用するセッションと同じであり、場合によっては共有できます。以前は、システム・クラスロード・クラスの場合のみ共有できていました。Oracle Database 11gからは、システム管理者の判断により、ユーザー・クラスロード・クラスのクラス・メタデータも共有できます。

同じクラスの二重定義

同じスキーマに同じ名前の2つのクラス・オブジェクトは指定できません。この規則によって、次の2つの制限を受けることになります。


注意:

例外は、データベース常駐JARの-prependjarnamesオプションを使用する場合です。このオプションを使用する場合、同じスキーマに同じ名前の2つのクラスを指定できます。データベース常駐JARの詳細は、「データベース常駐JAR」を参照してください。

  • 特定の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ファイルを渡すと、それはアーカイブをオープンし、そのメンバーを個別にロードします。JARまたはZIPのスキーマ・オブジェクトはありません。JARまたはZIPの内容が前回のロード時から変更されていない場合は、再ロードされません。したがって、JARファイルまたはZIPファイルのロードによるパフォーマンス低下は、ほとんどありません。実際には、JARファイルまたはZIPファイルのロードが、loadjavaツールを使用する最も簡単な方法です。


注意:

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

データベース常駐JAR

11gリリース1(11.1)以降では、データベースにJARの内容をロードする場合、JAR自体を表すデータベース・オブジェクトを作成するオプションが用意されています。この方法で、JARオブジェクトと、JARからロードされたクラス、リソースおよびソース・オブジェクトとの関連付けを保持できます。その結果、次のことが可能です。

  • 標準的なJVMと同じ方法で、符号付きJARとJARネームスペースの分離を使用します。

  • JARから導出したクラスを管理する一方で、それを1つの単位としてデータベースにロードします。これは、JARからロードされたクラスを個別に再定義しない場合に便利です。また、外部ファイルシステム上のJARの内容や存続に関係なく、クラスの削除時にJARからロードされたクラス・セット全体を削除できます。

JARをデータベースにロードするには、loadjavaツールの次のオプションを使用します。

  • -jarsasdbobjects

  • -prependjarnames

loadjavaツール・オプションの詳細は、「loadjavaツール」の項を参照してください。

実行権限の付与

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

Javaアプリケーションを定義するクラスは、その所有者のSQLスキーマの下にある、Oracle Database内に格納されます。デフォルトでは、特定のユーザーのスキーマに常駐するクラスは、セキュリティ保護のために他のユーザーからは実行できません。次の方法で、自分のクラスの実行権限を他のユーザーに付与できます。

  • loadjava -grantオプションを使用します。


    関連項目:

    「loadjavaツール」

  • 次のコマンドを使用します。

    SQL> grant execute on myclass to HR;
    

    myclassは、基礎となるJavaクラスの名前です。


注意:

Oracle Database 11gリリース1 (11.1)以前では、ストアド・プロシージャの実行権限を付与する場合、ストアド・プロシージャと、そのストアド・プロシージャによって参照されるJavaクラスの両方の実行権限が付与されます。Oracle Database 11gリリース以降では、基礎となるJavaクラスの実行権限も付与する場合、そのクラスに対する実行権限を明示的に付与する必要があります。これは、セキュリティの向上を目的として実装されています。

図2-3は、クラスを実行するために必要な権限を示しています。

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

図2-3の説明が続きます。
「図2-3 クラスの実行権限」の説明


関連項目:

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

現行ユーザーの制御

PL/SQLコードの実行中は、常に現行ユーザーが存在します。Javaコードの実行にも同じ概念が使用されます。まず、現行ユーザーは、Javaコードを起動するセッションを作成するユーザーです。Javaメソッドは、対応するラッパーによってSQLまたはPL/SQLからコールされます。Javaラッパーは、JavaメソッドをPL/SQLストアド・プロシージャまたはファンクションとしてSQLおよびPL/SQLに公開する特殊なPL/SQLエンティティです。このようなラッパーは、有効な現行ユーザーを変更できます。有効な現行ユーザーをラッパーの所有者に変更するラッパーは、定義者権限ラッパーと呼ばれます。ラッパーが有効な現行ユーザーを変更しない場合、有効なユーザーはそのまま変わりません。

Javaラッパーは、デフォルトで定義者権限ラッパーです。これをオーバーライドするには、AUTHID CURRENT_USERオプションを使用してラッパーを作成します。


関連項目:

AUTHID CURRENT_USERオプションの詳細は、「トップレベルのコール仕様の作成」を参照してください。

Javaコードの実行中は常に、Javaコール・スタックが保持されます。スタックには、現在実行中のメソッドに対応する最も内側のフレームを含め、入力したJavaメソッドに対応するフレームがあります。デフォルトでは、Javaメソッドは、スタックで、現行ユーザーを変更せずに、つまり定義者権限ではなく現在の有効な実行者権限で、実行されます。

Javaクラスは、loadjava -definerオプションを使用してデータベースにロードできます。定義者属性がマークされているクラスのメソッドは、定義者権限メソッドになります。このようなメソッドが入力されると、定義者フレームと呼ばれる特殊なフレームがJavaスタックに作成されます。このフレームは、有効な現行ユーザーをこのようなクラスの所有者(定義者)に切り替えます。新しいユーザーIDは、定義者フレームがスタックからなくなるまで、あるいはネストされた定義者フレームが入力されるまで、すべての内部フレームに対して有効です。

したがって、SQLまたはPL/SQLからそのラッパーによってコールされたJavaメソッドの実行中は常に、有効なユーザーは次のいずれかになります。

  • Javaスタックの最内部の定義者フレーム

  • 最上部JavaメソッドのPL/SQLラッパーの所有者(定義者権限の場合)またはラッパーをコールしたユーザー。

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

図2-4に示すように、すべての地区サイトで同じプロシージャを使用して、各地区のsales表を問合せできます。

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

図2-4の説明が続きます。
「図2-4 実行者権限のソリューション」の説明

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

図2-5に示すように、analyzeプロシージャから定義者権限プロシージャcalcCommをコールし、このプロシージャでpayroll表を更新することで解決します。

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

図2-5の説明が続きます。
「図2-5 間接的なアクセス」の説明

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は別名です。30文字を超える完全修飾名は別名で格納されます。


関連項目:

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

サーバーでスキーマ・オブジェクトの別名を使用する場合は、別名や変換規則が不明であっても、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

この文を実行すると、javasnmビューに保存されたすべてのデータが表示されます。

状態(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エントリを検索します。

公開

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

監査

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

他のDDL文と同様に、Oracle Database 10gリリース2(10.2)以降では、ソース、クラスおよびリソースのJavaスキーマ・オブジェクトを作成、変更または削除するDDL文を監査できます。Oracle Databaseには、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言語リファレンス』


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

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で実行中のアプリケーションは、サーバー上でイメージを物理的に表示しようとしないかぎり、そのイメージを解析、操作および出力できます。標準の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に完全に準拠します。

クラスの短縮名

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

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

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

Oracle DatabaseのClass.forName()

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

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

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

  • JDKは、ClassLoaderの1つのインスタンスを使用して、環境変数CLASSPATHに指定されている一連のディレクトリ・ツリー・ルートを検索します。

  • Oracle JVMは、クラスの検索方法を指定する複数のリゾルバを定義します。各クラスにはリゾルバが対応付けられており、各クラスは異なるリゾルバを保持できます。Class.forName()をコールするメソッドを実行すると、現在実行中のクラス(this)のリゾルバを使用してクラスが検索されます。

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

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

  • Classオブジェクト自体を指定してクラス名の参照を回避します。

  • Class.forName()メソッドに対してClassLoaderインスタンスを指定します。

  • classForNameAndSchema()メソッドに対してクラスとそのクラスが常駐するスキーマを指定します。

  • ClassForName.lookupClass()に対してスキーマとクラス名を指定します。

  • スキーマ名とクラス名でオブジェクトをシリアライズします。


注意:

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

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

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);
  ...
}

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

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

例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);
}

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

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

"<schema>:<class>"

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

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

注意:

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

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

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

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

  • oracle.aurora.rdbms.DbmsObjectOutputStream

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

  • oracle.aurora.rdbms.DbmsObjectInputStream

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

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()メソッドにクラス名とスキーマ名を渡します。

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

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

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

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

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

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

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

関連項目: 「メモリー領域管理」

ファイルとソケット Javaにはファイル・リソースまたはソケット・リソースを表すクラスが含まれています。これらのクラスのインスタンスは、ファイル・ハンドルなど、オペレーティング・システムのファイルまたはソケットの構成メンバーを保持します。
スレッド Oracle JVMスレッドでは、データベースによる複数の同時実行セッションのサポートで提供される機能に対して追加のスケーラビリティは提供されません。ただし、Oracle JVMは、Javaスレッドの完全なAPIをサポートしています。

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


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

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


関連項目:

Java 2セキュリティ

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

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

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

メモリーが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が終了するまでリソースが解放されない可能性があります。メソッドの中でリソースをクローズすることが最も確実な方法です。

Oracle DatabaseでのRuntime.exec機能の使用

Java仮想マシンは、Java Standard Editionのjava.lang.Runtime.execメソッドのファミリーを完全にサポートしています。これらのメソッドによって、ユーザーが指定したコマンドを実行するための新しいオペレーティング・システム(OS)プロセスが生成されます。サーバーでは、これらのメソッドを慎重に使用する必要があります。Java仮想マシンでは、OSコマンドの実行許可はデフォルトですべてのデータベース・ユーザーには付与されず、権限のある管理者のみがこれを発行します。DBAは、「Oracle DatabaseでのRuntime.exec機能のセキュアな使用」の項を参照して、推奨事項に従ってください。また、これらの許可をデータベース・ユーザーに発行する際には慎重に行う必要があります。

JMXを使用したアプリケーションの管理

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

JMXの概要

JMX(Java Management Extensions)は、アプリケーション、システム・オブジェクト、デバイス、サービス指向のネットワーク、JVM(Java仮想マシン)を管理および監視するツールを提供するJavaテクノロジです。このAPIを使用すると、そのクラスの動的な作成と変更が可能です。そのため、リソースが作成、インストールおよび実装されると、このテクノロジを使用してリソースを監視および管理できます。また、JMX APIにはリモート・アクセスが含まれているため、リモート管理プログラムはこのような目的で実行中のアプリケーションと対話できます。

JMXでは、特定のリソースは、MBean(マネージドBean)と呼ばれる1つ以上のJavaオブジェクトで構成されます。これらのMBeanは、管理エージェントとして機能するコア・マネージド・オブジェクト・サーバー(MBeanサーバーと呼ばれる)に登録され、Javaプログラミング言語に対応するほとんどのデバイスで実行できます。JMXエージェントは、MBeanが登録されるMBeanサーバーと、MBeanを処理するサービスのセットで構成されます。

セッションでのJMXの有効化と起動

Javaを実行しているセッションでJMXサービスを有効化および実行するために、JMXSERVERロールおよびdbms_java.start_jmx_agentプロシージャが提供されています。JMXSERVERロールには、セッションでMBeanServerとJMXエージェントを起動および実行できる特定のJavaパーミッションが付与されます。プロシージャdbms_java.start_jmx_agentは、特定のセッションで、セッションの期間内は通常アクティブのままのエージェントを起動します。JMXを有効にして起動するには、次の操作を実行します。

  1. SYSまたはSYSTEMからJMXSERVERを取得します。

    SQL> grant jmxserver to HR;
    

    HRはユーザー名です。

  2. プロシージャdbms_java.start_jmx_agentを起動して、セッションでJMXを起動します。dbms_java.start_jmx_agentプロシージャは、次の引数を使用して起動できます。

    port: JMXリスナーのポート。このパラメータの値は、Javaプロパティcom.sun.management.jmxremote.portを設定します。

    ssl: Javaプロパティcom.sun.management.jmxremote.sslの値を設定します。値がtrueおよびfalseの場合は、無視されます。

    auth: プロパティcom.sun.management.jmxremote.authenticateの値で、それ以外の場合は、Java Authentication and Authorization Service (JAAS)資格証明のセミコロンで区切られたリスト。値は大/小文字が区別されません。

    各引数は、nullをデフォルト値としてnullにするか、または省略できます。引数がnullの場合、セッションの対応するプロパティの以前からある値は変更されません。


    注意:

    dbms_java.start_jmx_agentのパラメータに対応するJavaプロパティは、標準のJava 5.0 JMXドキュメントで指定されたJavaプロパティのセットからのものです。Java JMXプロパティの完全なリストは、http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.htmlを参照してください。

    dbms_java.start_jmx_agentプロシージャは、OJVM JMXサーバーおよびリスナーをアクティブ化するエージェントを起動します。JMXサーバーは、現行セッションで1つ以上のデーモン・スレッドとして実行され、通常、セッションの期間内は使用可能です。JMXエージェントをセッションで起動すると、セッションで実行中のJavaコードを監視できます。

    dbms_java.start_jmx_agentプロシージャは、Javaメソッドoracle.aurora.rdbms.JMXAgent.startOJVMAgentのPL/SQLラッパーで、これは、Javaストアド・プロシージャからプログラムでコールすることもできます。startOJVMAgentメソッドは、JMXサーバーとJMX接続デーモン・スレッドを起動してから終了します。専用サーバーでは、これらのスレッドはセッションの存続期間中はアクティブ状態を維持できますが、コールとコールの時間間隔は非活性状態になります。これらの時間間隔が短い場合、同じソケット接続が透過的に再開されます。これによって、JConsoleなどのクライアントは複数のコールにわたって接続を維持できます。

    JMX監視の別のモードが、EXIT_CALL_WHEN_ALL_THREADS_TERMINATEポリシーで可能になります。コール終了ポリシーにOracleRuntime.EXIT_CALL_WHEN_ALL_THREADS_TERMINATEを設定すると、Javaコールがプログラムで終了されるまでstartOJVMAgentメソッドを起動するコールでJMXサーバーを引き続き実行するようにセッションを構成できます。このモードは、様々なJavaタスクが特定のMBeanの操作としてJMXクライアントから起動されている場合に便利です。このように、継続的なJMXの管理および監視がこれらの操作によって実行されます。jmxserv.Loadなど、このようなBeanのJVM JMXデモを参照してください。

Oracle JVM JMXのデフォルト設定と構成可能性

dbms_java.start_jmx_agentをアクティブにする場合、プロパティcom.sun.management.jmxremotetrueに設定されます。start_jmx_agentを起動する前に、JMXSERVER権限のユーザーは、次の方法で様々な管理プロパティを初期設定できます。

  • PL/SQLファンクションdbms_java.set_propertyを使用します。

  • メソッドjava.lang.System.setPropertyを起動します。

JMXSERVERロールのユーザーは、Javaプロパティcom.sun.management.config.fileで指定されたデータベース常駐Javaリソースのプロパティも初期設定できます。com.sun.management.config.fileが設定されていない場合、このリソースのデフォルト名はlib.management.management.propertiesです。このリソース・メカニズムは、標準のファイルベースのJMX構成管理のOracle JVM拡張機能です。これは、セキュリティの向上とスキーマごとの管理を提供するため、Oracle JVMにとって優れたメカニズムです。リソースがスキーマに存在しない場合、ファイルの読取りはフォールバックとして行われます。com.sun.management.config.fileが設定されていない場合、デフォルトのファイル・パスは$(java.home)/lib/management/management.propertiesです。Oracle Database 12cでは、このファイルに次の初期設定が含まれています。

com.sun.management.jmxremote.ssl.need.client.auth = true
com.sun.management.jmxremote.authenticate = false

プロパティcom.sun.management.jmxremote.ssl.need.client.authcom.sun.management.jmxremote.sslを併用すると、クライアントおよびサーバー証明書を使用する、双方向暗号化SSL認証のJMXが設定されます。com.sun.management.jmxremote.sslのデフォルト値はtrueです。この構成はデフォルトであり、JAASパスワード認証よりも好まれます。


注意:

パスワードのファイルベース・ストアを提供するデフォルトのJMXログイン・モジュールは、セキュリティ上の理由からOracle JVMでサポートされていません。そのため、SSLクライアント認証のかわりにJAASパスワード認証を使用する必要がある場合、この項で示すように、一時的なJAAS資格証明をauthパラメータとしてdbms_java.start_jmx_agentに安全に渡すか、または安全なカスタムLDAPログイン・モジュールを使用するようにJMXを構成します。

dbms_java.start_jmx_agentへのSQLコールの例

JMXサーバーの起動例を次に示します。

  • 前述の項で説明したようなデフォルトの設定、または同じセッションの以前に設定された値を使用してJMXサーバーおよびリスナーを起動します。

    call dbms_java.start_jmx_agent();
    
  • 前述の項で説明したようなデフォルトの設定、または同じセッションの以前に設定された値を使用してJMXサーバーおよびリスナーを起動します。

    call dbms_java.start_jmx_agent(null, null, null);
    
  • JMXサーバーおよびリスナーをポート9999で起動します。他のJMX設定は、デフォルト値または同じセッションで以前に設定された値です。

    call dbms_java.start_jmx_agent('9999');
    
  • JMXサーバーおよびリスナーをポート9999で起動します。他のJMX設定は、デフォルト値または同じセッションで以前に設定された値です。

    call dbms_java.start_jmx_agent('9999', null, null);
    
  • JMXサーバーおよびリスナーを起動します。JMX設定は、デフォルト値または同じセッションで以前に設定された値です。JAAS資格証明monitorRole/1z2xおよびcontrolRole/2p3oを使用します。

    call dbms_java.start_jmx_agent(null, null, 'monitorRole/1z2x;controlRole/2p3o');
    

    これらの資格証明は一時的なものです。プロパティcom.sun.management.jmxremote.authenticatetrueに設定されています。

  • JMXリスナーを、SSLやJAAS認証は使用せず、ポート9999で起動します。開発またはデモンストレーションの場合にのみ使用します。

    call dbms_java.start_jmx_agent('9999', 'false', 'false');
    

JConsoleを使用したOracle JVMの監視および制御

この項では、JConsole(標準のJMXクライアント・ツール)を使用して、Oracle JVMを監視および制御する方法について説明します。JConsoleは、標準のJava JDKの構成要素です。

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


注意:

JConsoleを使用してデータベース内のJavaを監視するには、サーバー側のJavaセッションでJMXエージェントが実行されている必要があります。詳細は、「セッションでのJMXの有効化と起動」を参照してください。

jconsoleコマンド

JConsoleを起動するにはjconsoleコマンド構文を使用します。JConsoleツールを開始する最も簡単な書式は次のようになります。

jconsole [hostName:portNum]

各項目は次のとおりです。

  • hostnameは、アプリケーションを実行しているシステムの名前です。

  • portNumは、JMXリスナーのポート番号です。

次の例では、デフォルトのポート9999を使用してexample.comという名前のホストに接続します。このモードでは、認証や暗号化は行われません。このモードは、デモやテストの場合のみに適しており、このモードを使用して、次のコマンドで開始するOracle JVMのJMXセッションに接続できます。

call dbms_java.start_jmx_agent(portNum, false, false);

サーバーのデーモン・スレッドが実行中で休止していない場合にのみ、JConsoleからOracle JVMに接続し対話できます。つまり、指定されたポートでJMXサーバーを実行するセッションに、アクティブなJavaコールが存在する必要があります。後続のJavaコール間の時間間隔中にJMXサーバーはその状態と統計情報を保持しますが、JConsoleと通信できません。

JConsoleインタフェース

JConsoleインタフェースは、次のタブで構成されています。


重要:

Oracle Database 12cリリース1 (12.1)では、収集されてJConsoleに渡されるデータは、JMXエージェントを実行するOracle JVMセッションに限定されます。このデータには、Oracle JVMで実行している他のセッションの属性は含まれません。OracleRuntime MBeanは例外で、WholeJVM_ AttributesおよびOracle JVMの操作についての情報を多数提供します。OracleRuntime MBeanの詳細は、「OracleRuntime MBean」を参照してください。

Oracle JVMの概要情報の表示

JConsoleインタフェースの「Summary」タブを使用して、Oracle JVMの概要情報を表示できます。このタブには、スレッドの使用状況、メモリー使用量、クラスのロード、VMやオペレーティング・システムのその他の仕様に関する主な監視情報が表示されます。

JMXエージェントを実行するOracle JVMセッションとJConsoleとの接続が成功すると、次の図のような「Overview」タブが表示されます。

図2-6 JConsoleインタフェースの「Overview」タブ

図2-6の説明が続きます。
「図2-6 JConsoleインタフェースの「Overview」タブ」の説明

表2-10で、「Overview」タブに表示されるフィールドについて説明します。

表2-10 JConsoleインタフェースの「Overview」タブのフィールドの説明

フィールド 説明

稼働時間

Oracle JVMセッションの実行時間。

プロセスCPU時間

Oracle Database 12cリリース1 (12.1)では、Oracle JVMセッションに関するこの情報は収集されません。

ライブ・スレッド

有効なデーモン・スレッドと非デーモン・スレッドの現在の数。

ピーク

Oracle JVMの起動以降の有効なスレッドの最大数。

デーモン・スレッド

有効なデーモン・スレッドの現在の数。

開始合計

Oracle JVMを起動してから開始したスレッドの合計数。デーモン・スレッド、非デーモン・スレッド、終了したスレッドが含まれます。

現在のヒープ・サイズ

現在ヒープで占められているKB数。

コミット済メモリー

ヒープで使用するために割り当てられるメモリーの合計。

最大ヒープ・サイズ

ヒープで占められる最大KB数。

ファイナライズを保留中のオブジェクト

最終段階に向けて保留中のオブジェクトの数。

ガベージ・コレクタ情報

ガベージ・コレクタに関する情報。名前、実行したコレクションの数、ガベージ・コレクションの実行に費やす合計時間などです。

現在ロードされているクラス

実行するために、現在メモリーにロードされているクラスの数。

ロードされているクラスの合計

セッションを開始してから、セッション・メモリーにロードされたクラスの合計数。

アンロードされているクラスの合計

メモリーからアンロードされたクラスの数。Oracle Database 12cリリース1 (12.1)の場合、通常はゼロになります。

合計物理メモリー

Oracle Database 12cリリース1 (12.1)では、Oracle JVMセッションに関するこの情報は収集されません。そのため、表示される値はゼロになります。

空き物理メモリー

Oracle Database 12cリリース1 (12.1)では、Oracle JVMセッションに関するこの情報は収集されません。そのため、表示される値はゼロになります。

コミット済仮想メモリー

Oracle Database 12cリリース1 (12.1)では、Oracle JVMセッションに関するこの情報は収集されません。そのため、表示される値はゼロになります。


メモリー使用量の監視

JConsoleインタフェースの「Memory」タブを使用して、メモリー使用量を監視できます。このタブには、メモリー使用量とメモリー・プールに関する情報が表示されます。

図2-7に「Memory」タブを示します。

図2-7 JConsoleインタフェースの「Memory」タブ

図2-7の説明が続きます。
「図2-7 JConsoleインタフェースの「Memory」タブ」の説明

「Memory」タブのグラフには、メモリー領域全体だけでなく特定のメモリー・プールに関するOracle JVMのメモリー使用量と時間が示されます。Oracle JVMに使用可能なメモリー・プールは、Oracle JVMの内部組織を反映し、Oracle JVMのメモリー・マネージャのオブジェクト・メモリーに対応しています。このリリースのOracle Databaseで使用可能なメモリー・プールは次のとおりです。

  • New Generation領域

    大部分のオブジェクトに対してメモリーが最初に割り当てられるメモリー・プールです。Eden領域とも呼ばれます。

  • Old Generation領域

    このメモリー・プールには、Eden領域にガベージ・コレクション・プロセスが残ったオブジェクトが含まれます。Survival領域とも呼ばれます。

  • Malloc/Free領域

    このメモリー・プールには、メモリーがmallocで割り当てられ、freeで解放されるオブジェクトが含まれます。

  • End of Migration領域

    このメモリー・プールには、セッション終了時の移行を残すオブジェクトが含まれます。

  • Dedicated Session領域

    このメモリー・プールは、Oracle Dedicated Sessionsモードでセッション・オブジェクトにメモリーを割り当てる場合に使用します。

  • Paged Session領域

    このメモリー・プールは、大きなサイズのセッション・オブジェクトが呼び出される場合、そのオブジェクトへのメモリーの割当てに使用します。

  • Run領域

    このメモリー・プールは、一時オブジェクトおよび補助オブジェクトにメモリーを割り当てる場合に使用します。

  • Stack領域

    このメモリー・プールは、スタックに似た方式でメモリーの割当てと解放が行われる一時オブジェクトにメモリーを割り当てる場合に使用します。

「Memory」タブの「Details」領域には、次の内容で現在のメモリーのマトリクスが表示されます。

  • 使用済

    セッションを実行するプロセスで現在使用されているメモリー量を示します。

  • コミット済

    メモリーがすでに割り当てられているかのように、Oracle JVMでの使用が保証されたメモリー量を示します。コミットされるメモリーの量は、時間の経過とともに変わります。ただし、コミットされるメモリーは、必ず使用済メモリー以上になります。

  • 最大

    メモリーの管理に使用できる最大メモリー量を示します。通常は、Oracle JVMの初期構成に対応しています。

「Memory」タブの右下隅にある棒グラフは、各メモリー・プールで消費されたメモリーを示します。使用済のメモリーがメモリー使用量のしきい値を超えると、棒グラフは赤に変わります。メモリー使用量のしきい値は、MemoryMXBeanの属性を使用して設定できます。詳細は、「メモリーのしきい値」を参照してください。

スレッドの使用状況の監視

JConsoleインタフェースの「Threads」タブを使用して、スレッドの使用状況を監視できます。

図2-8 JConsoleインタフェースの「Threads」タブ

図2-8の説明が続きます。
「図2-8 JConsoleインタフェースの「Threads」タブ」の説明

「Threads」タブのグラフには、有効なスレッドの数と時間が表示されます。特定のタイプのスレッドが特定の色で示されています。

  • マゼンタは、スレッドの合計数を表します。

  • 赤は、スレッドの最大数を表します。

  • 青は、有効なスレッドの数を表します。

このタブのスレッドのリストには、有効なスレッドが表示されます。リストのスレッドを選択すると、そのスレッドに関する情報が右側のペインに表示されます。スレッドの名前、状態、スタック・トレースなどの情報があります。

「Filter」フィールドは、スレッドの絞込みに役立ちます。

クラスのロードの監視

JConsoleインタフェースの「Classes」タブを使用して、クラスのロードを監視できます。このタブのグラフには、ロードされたクラスの数と時間が描画されます。

図2-9 JConsoleインタフェースの「Classes」タブ

図2-9の説明が続きます。
「図2-9 JConsoleインタフェースの「Classes」タブ」の説明

MBeanの監視および管理

「MBeans」タブを使用して、MBeanを監視および管理できます。このタブには、プラットフォームMBeanサーバーに登録されたすべてのMBeanに関する情報が表示されます。

「MBeans」タブの左ペインのツリーはMBeanツリーと呼ばれ、オブジェクト名に従って体系化されたすべてのMBeanが表示されます。MBeanツリーでMBeanを選択すると、その属性、操作、通知、その他の情報が右側のペインに表示されます。たとえば、図2-10では、左側のMBeanツリーで「Old Generation」MemoryPool MBeanを選択しており、「Old Generation」MemoryPool MBeanの属性が右側に表示されています。

図2-10 MBeanの属性の表示

図2-10の説明が続きます。
「図2-10 MBeanの属性の表示」の説明

属性の値は、書込み可能な場合は設定できます。書込み可能な値は青で表示されています。たとえば、図2-10では、属性CollectionUaageThresholdおよびUsageThresholdが書込み可能です。

属性の値をダブルクリックすると、属性の値と時間を示すグラフも表示されます。たとえば、「GCManager」MBeanの「CollectionTime」プロパティの値をクリックすると、図2-11のようなグラフが表示されます。

図2-11 属性の値のグラフの表示

図2-11の説明が続きます。
「図2-11 属性の値のグラフの表示」の説明

属性をクリックすると、複雑な属性の詳細を表示できます。たとえば、図2-12に示すように、メモリー・プールの属性「Usage」および「PeakUsage」の詳細を表示できます。

図2-12 「MBeans」タブの複雑な属性の詳細の表示

図2-12の説明が続きます。
「図2-12 「MBeans」タブの複雑な属性の詳細の表示」の説明

MBeanの「Operations」タブには管理機能のインタフェースがあります。たとえば、「Perform Garbage Collection」をクリックすると、メモリー・プールまたはメモリー・マネージャに対してガベージ・コレクションを開始できます。Oracle JVMのJMXデモ(javavm/demo/jmx/)には、Oracle JVMにロードされる追加のカスタムMBeanが複数用意されています。次の例で、「DBProps」MbeanのgetProp操作の結果を示します。

図2-13 JConsoleインタフェースの「MBeans」タブの「Operations」タブ

図2-13の説明が続きます。
「図2-13 JConsoleインタフェースの「MBeans」タブの「Operations」タブ」の説明

VM情報の表示

JConsoleインタフェースの「VM」タブを使用して、VM情報を表示できます。

図2-14 JConsoleインタフェースの「VM」タブ

図2-14の説明が続きます。
「図2-14 JConsoleインタフェースの「VM」タブ」の説明

OracleRuntime MBean

Oracle Database 11gリリース2(11.2)以降では、dbms_java.start_jmx_agentプロシージャをコールすると、新規のMBean(OracleRuntime)がOracle JVMプラットフォームMBeanのリストに追加されます。このMBeanはOracle JVMに固有のものです。

OracleRuntime MBeanの「Attributes」タブには、oracle.aurora.vm.OracleRuntimeクラスによって操作されるほとんどのパラメータが公開されます。図2-15に、OracleRuntime MBeanの「Attributes」タブを示します。

図2-15 OracleRuntime MBeanの「Attributes」タブ

図2-15の説明が続きます。
「図2-15 OracleRuntime MBeanの「Attributes」タブ」の説明

黒で表示されるパラメータは読取り専用のため、変更できません。たとえば、JavaPoolSizePlatformなどです。青の値は読取り/書込みで、値が変更できることを表します。OracleRuntime MBeanのほとんどの属性は、現在のセッションに対してローカルです。

OracleRuntime MBeanのWholeJVM_属性はグローバルです。これらの属性は、パフォーマンス・ビューv$sessionおよびv$sesstatからの収集に従って、データベース・インスタンス内のすべてのJava対応セッションにおけるOracle JVMのメモリー使用量統計の合計を反映します。図2-16に、OracleRuntime MBeanのWholeJVM_属性を示します。

図2-16 OracleRuntime MBean

図2-16の説明が続きます。
「図2-16 OracleRuntime MBean」の説明

OracleRuntime MBeanの「Operations」タブには、oracle.aurora.vm.OracleRuntimeクラスの操作の多くが公開されます。

さらに、図2-17図2-18に示すように、sessionsRunningJava操作およびsessionDetailsBySID操作を使用して、Javaが有効な特定のデータベース・セッションの各メモリー使用量の統計を監視できます。

図2-17 sessionsRunningJava操作

図2-17の説明が続きます。
「図2-17 sessionsRunningJava操作」の説明

図2-18 sessionDetailsBySID操作

図2-18の説明が続きます。
「図2-18 sessionDetailsBySID操作」の説明

メモリーのしきい値

使用量のしきい値は、メモリー・プールの管理可能な属性です。コレクション使用量のしきい値は、ガベージ・コレクションされた一部のメモリー・プールの管理可能な属性です。プールの対応するしきい値のチェックを有効にするには、各属性を正の値に設定します。しきい値をゼロに設定すると、メモリー・プールのしきい値のチェックが無効になります。デフォルトでは、Oracle JVMのすべてのプールのしきい値のチェックは無効になっています。

使用量のしきい値とコレクション使用量のしきい値は、「MBeans」タブで設定されます。たとえば、左ペインのツリーからメモリー・プール「Old Generation」を選択し、このメモリー・プールの使用量のしきい値を20MBに設定して、コレクションのしきい値を1MBに設定した場合、しばらくすると、図2-19に示すように、しきい値交差イベントの数がカウントに表示されます。

図2-19 「MBeans」タブの使用量のしきい値とコレクション使用量のしきい値の設定

図2-19の説明が続きます。
「図2-19 「MBeans」タブの使用量のしきい値とコレクション使用量のしきい値の設定」の説明

メモリー・プール「Old Generation」のメモリー使用量が20MBを超えると、JConsoleインタフェースのメモリー・プール「Old Generation」を表すグラフの一部が赤に変わります。赤い部分は、使用済メモリーの一部が使用量のしきい値を超えたことを示します。図2-20に示すように、ヒープ・メモリーを表すグラフも赤に変わります。

図2-20 使用済メモリーが使用量のしきい値を超えた場合のJConsoleインタフェースの「Memory」タブ

図2-20の説明が続きます。
「図2-20 使用済メモリーが使用量のしきい値を超えた場合のJConsoleインタフェースの「Memory」タブ」の説明

セキュリティに関する重要な注意点

SSLと認証を無効にしてリモート・リスナーを起動すると、一般的なセキュリティのガイドラインに違反するため、攻撃に対してサーバーが脆弱になります。そのため、本番環境ではこのようなモードを常に使用しないことをお薦めします。このモードは、JDKとの互換性や開発に対してサポートされます。Oracle JVMでJMXを本番で使用する場合は、安全なJMX接続を使用する必要があります。

セキュリティ関連のプロパティ値をdbms_java.set_propertySystem.setPropertyまたはdbms_java.start_jmx_agentに指定する場合、非エコー・リスナーを使用するか、またはOracle Application Serverなどの安全なアプリケーション層から暗号化されたJDBC接続によってこれらを起動します。パスワードは、クリアテキスト・ファイルで保存しないでください。Oracleウォレットを使用して、証明書を作成および管理します。セキュリティを向上させるには、SSL認証のクライアント証明書を使用します。


関連項目:

Oracleデータベースのセキュリティ機能の詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください。

JMXの共有サーバーの制限事項

専用モード・サーバーでは、セッションの継続期間中のJMX接続がサポートされています。共有サーバーのJMX接続は、通常は単一のコールに限定されます。この制限は、JMX接続が本質的にスレッドやソケットなどのオペレーティング・システム・リソースに依存していることに主に起因しています。これらのリソースは、共有サーバーのコールの境界を越えて存続することはありません。したがって、単一のコールの存続期間中のみのJMX接続が完全にサポートされています。


注意:

この制限はエージェント接続にのみ影響し、それに登録されているMBeanSrverやMbeansの状態には影響しません。MBeanSrverとMbeansの状態、および特にその統計は、共有サーバーのコールの境界を越えて保持されます。

専用サーバー・モードを使用できない場合でも、次のガイドラインに従って、JMX接続を確立し、共有サーバーを監視できます。

  • JMXの管理アクティビティと監視アクティビティがすべて1つのJavaコールで行われるように計画します。

  • DBMS_JAVA.set_propertyファンクションをコールしてcom.sun.management.jmxremote.portプロパティを設定したり、DBMS_JAVA.start_jmx_agentメソッドを使用しないでください(これらのコールによってJMXがアクティブ化され、共有サーバーのコール境界が生成されるためです)。そのかわりに、監視対象のJavaコードから直接oracle.aurora.rdbms.JMXAgent.startOJVMAgentメソッドをコールして、JMXエージェントを起動してください。com.sun.management.jmxremote.portプロパティの値をstartOJVMAgentメソッドに渡す必要があります。com.sun.management.jmxremote.portプロパティ以外のJMX関連プロパティはJMXエージェントを起動するものでなく、任意の方法を使用して設定できます。

Oracle Databaseのスレッド

Oracle JVMは、単一クライアントの非プリエンプティブ・スレッド・モデルであるデータベース・セッション・モデルに基づきます。Oracle DatabaseのJavaではスレッド・プログラムを実行できますが、実行レベルではシングル・スレッドです。このモデルのJVMは、データベース・セッションに関連するすべてのJavaスレッドを1つのオペレーティング・システム・スレッドで実行します。一度ディスパッチすると、Thread.yield()をコールしてスレッドが明示的に生成され、Socket.read()をコールしてスレッドがブロックされるまで、または実行エンジンによって切り替えられるまで、スレッドは実行を続けます。スレッドが生成され、ブロックされるか、または切り替えられると、JVMは別のスレッドをディスパッチします。


注意:

11gリリース1(11.1)以降では、Oracle JVMはスレッドの切り替えをサポートします。スレッドの切り替えはJava仕様で義務付けられていませんが、JDK1.5にある新しいjava.util.concurrent APIを適切にサポートするために必要です。

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

  • システム・コールを最小限に抑制します。Oracle JVMでは、標準のシステム・コールのかわりに非システム・ソリューションが採用されました。たとえば、システム・コールを使用せずにモニター同期ブロックやメソッドに入ることが可能です。

  • デッドロックを検出します。

    • Oracle JVMはスレッド間のデッドロックを監視します。デッドロックが発生すると、Oracle JVMはいずれかのスレッドを終了し、oracle.aurora.vm.DeadlockError例外をスローします。

    • シングル・スレッド・アプリケーションは中断できません。スレッドが1つのみのアプリケーションを中断しようとすると、oracle.aurora.vm.LimboError例外がスローされます。

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

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

  • スレッドがコール元に戻された場合。

  • 例外がスローされ、Javaコードで捕捉されなかった場合。

  • System.exit()OracleRuntime.exitSession()またはoracle.aurora.vm.OracleRuntime.exitCall()メソッドがコールされた場合。

  • DBMS_JAVA.endsession()またはDBMS_JAVA.endsession_and_related_state()メソッドがコールされた場合。

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

  • メイン・スレッドがコール元に戻るか、またはこのスレッドで捕捉されない例外がスローされ、いずれの場合も、他のすべての非デーモン・スレッドは処理されます。非デーモン・スレッドは、その初期メソッドから戻されるか、スレッドで捕捉されない例外がスローされることで終了します。

  • いずれかのスレッドでSystem.exit()OracleRuntime.exitSession()またはoracle.aurora.vm.OracleRuntime.exitCall()メソッドをコールします。

  • DBMS_JAVA.endsession()またはDBMS_JAVA.endsession_and_related_state()メソッドをコールします。

11gリリース1(11.1)以前は、System.exit()またはoracle.aurora.vm.OracleRuntime.exitCall()をコールすることでコールが終了した場合、Oracle JVMは、専用サーバー・モードと共有サーバー・モードの両方でコールをただちに終了し、すべてのスレッドを終了していました。11gリリース1(11.1)以降では、DBMS_JAVAパッケージに次のPL/SQLファンクションを追加することでこの問題に対応しています。

  • FUNCTION endsession RETURN VARCHAR2;

  • FUNCTION endsession_and_related_state RETURN VARCHAR2;

コール時に、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()をコールします。

    • 値をfalse(デフォルト値)に設定すると、すべてのスレッドは休止状態になりますが、正常に終了するためのThreadDeath例外が発生します。

    • 値がtrueの場合、すべてのスレッドがただちに終了します。

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

  • OracleRuntime.EXIT_CALL_WHEN_MAIN_THREAD_TERMINATES

    この値に設定した場合、メイン・スレッドが戻るか、またはメイン・スレッドで捕捉されない例外が発生するとすぐに、デーモンおよび非デーモンの両方を含む残りのすべてのスレッドは次の状態になります。

    • 共有サーバー・モードでは、threadTerminationPolicy()がtrueの場合、常に停止します。

    • threadTerminationPolicy()がfalseの場合、休止状態になります。

  • OracleRuntime.EXIT_CALL_WHEN_ALL_NON_DAEMON_THREADS_TERMINATE

    これはデフォルト値です。この値が設定されると、デーモン・スレッドのみが実行中である場合にコールが終了します。この時点では次のようになります。

    • threadTerminationPolicy()trueの場合、共有サーバー・モードでは、デーモン・スレッドは常に停止します。

    • threadTerminationPolicy()falseの場合、デーモン・スレッドは次のコールまで休止状態になります。これは、シャドウ(専用)サーバー・モードのデフォルト設定です。

  • OracleRuntime.EXIT_CALL_WHEN_ALL_THREADS_TERMINATE

    この値に設定した場合、すべてのスレッドが戻るか、捕捉されない例外が原因で終了する場合のみ、コールは終了します。この時点では、threadTerminationPolicy()の値に関係なくコールが終了します。


注意:

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

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


注意:

パフォーマンス上の理由から、専用サーバーをお薦めします。また、専用サーバーでは、複数のコールにわたって保持されるスレッドおよびソケットに依存するアプリケーションのクラスをサポートしています。たとえば、JMXエージェント接続機能などです。JMXエージェントの詳細は、「JMXを使用したアプリケーションの管理」を参照してください。

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

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

コール終了時の移行

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

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

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

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

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

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

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

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

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

コール終了時の最適化に対する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 you must
      // 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オブジェクトとそのインスタンスが同じオブジェクトになるため、オブジェクトの存続期間とそのインスタンスの存続期間が等しくなります。

EndOfCallRegistry.registerCallback()メソッド

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

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

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

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

public static void registerCallback(Callback thunk);

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

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

EndOfCallRegistry.runCallbacks()メソッド

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

static void runCallbacks()

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


注意:

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

Callbackインタフェース

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

Interface oracle.aurora.memoryManager.Callback

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

Callback.act()メソッド

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

public void act(Object value)

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

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

次の表に示すとおり、共有サーバー・モードでは、データベース・コールの終了時に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)で実行されます。


関連項目:

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

データベースに格納された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変数の値は、後続のコールでは正確さに欠ける可能性があります。

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