Oracle Databaseでは、標準のJavaアプリケーションが実行されます。ただし、Javaを統合したOracle Database環境は、一般的なJava開発環境とは異なります。この章では、Oracle DatabaseでのJavaアプリケーションの作成、インストールおよびデプロイについて、一般的なJava開発環境との基本的な相違について説明します。この章の内容は、次のとおりです。
Javaを統合したOracle Databaseでは、Javaアプリケーションはデータベース・セッションのコンテキスト内に存在します。Oracle JVMセッションは、従来のOracleセッションとまったく同様です。Oracle JVMの各セッションは、セッション内の複数のコールにわたって、クライアントがアクセスするJavaアプリケーションの状態を保持します。
図2-1に、各Javaクライアントが、データベース内のJavaアプリケーションを実行する環境としてデータベース・セッションを起動する方法を示します。Javaの各データベース・セッションには、独立したガベージ・コレクタ、セッション・メモリーおよびコール・メモリーがあります。
セッションのコンテキストで、クライアントは次の内容を実行します。
データベースに接続してセッションをオープンします。
データベース内でJavaを実行します。これはコールとも呼ばれます。
必要に応じてコールを実行し、セッション内で処理を続行します。
セッションを終了します。
1つのセッション内で、クライアントには独自のJava環境があります。クライアントには、セッションごとに個別のJVMが個々に起動されるように見えますが、実際の実装はそれよりも効率的です。セッション内では、Oracle JVMがアプリケーションのスケーラビリティを管理します。ある単一のクライアントからのすべてのコールは、そのクライアントのセッションで管理され、各クライアントからのコールは個別に処理されます。Oracle JVMは、クライアント間の読取り専用データの共有を最大にし、セッションごとの増分フットプリントを最小限にすることで、 複数のクライアントのパフォーマンスを最大にします。
基盤となるサーバー環境では、セッション、ネットワーク、状態および他の共有リソース管理の問題に関する詳細が、Javaコードに対して明示されません。static
として定義されたフィールドは、クライアントに対してローカルです。メモリーはセッションの境界を超えて使用できないため、クライアントは他のクライアントのstatic
フィールドにはアクセスできません。各クライアントは、それぞれのセッション内でJavaアプリケーション・コールを実行するため、各クライアントのアクティビティは他のクライアントとは切り離されています。コール中にオブジェクトを異なるクラスのstatic
フィールドに格納し、それを次のコールで使用できます。Javaプログラムの状態はすべてプライベートで、セッション全体にわたって存続します。
Oracle JVMは、セッション内で次の内容を管理します。
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()
が起動されます。
標準的な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 JVMでこれらを使用できるようにする必要があります。図2-2に示すように、loadjava
ツールを使用して、ソース・ファイルを標準のクラス・ファイルにコンパイルする、Oracle JVMのJavaコンパイラをコールできます。
また、図2-2では、loadjava
ツールを使用して、システム・データベース表に格納されているオプションの値を設定できることを示しています。これらのオプションは、特にJavaソース・ファイルの処理に影響を与えます。
各Javaクラスは、スキーマ・オブジェクトとして格納されます。オブジェクトの名前は、含まれているパッケージの名前も含めたクラスの完全修飾名から導出されます。たとえば、Handle
クラスのフルネームは、次のようになります。
oracle.aurora.rdbms.Handle
Javaスキーマ・オブジェクト名では、ピリオドがスラッシュで置換されるため、前述のクラスのフルネームは、次のようになります。
oracle/aurora/rdbms/Handle
Oracle Databaseでは、最大4000文字までのJava名を使用できます。ただし、Javaスキーマ・オブジェクトには30文字を超える名前は指定できません。したがって、スキーマ・オブジェクト名に30文字を超える名前が指定された場合は、スキーマ・オブジェクトの短縮名または別名が生成されます。それ以外の場合は、完全修飾名(フルネームとも呼ばれる)が使用されます。フルネームは、必要に応じて任意のコンテキストで指定できます。必要な場合は、名前のマッピングがOracle Databaseによって処理されます。
Javaソース・コードをいつコンパイルするかを決定します。
データベース内のJavaクラスの位置の特定に、デフォルトのリゾルバを使用するか、別のリゾルバを使用するかを決定します。
クラスをデータベースにロードします。クラスにデフォルトのリゾルバを使用しない場合は、ロード・コマンドで別のリゾルバを指定してください。
クラスまたはメソッドを公開します。
この項の内容は、次のとおりです。
Javaソース・コードのコンパイルは、次のいずれかの方法で実行できます。
クライアント・システムでソースを明示的にコンパイルしてから、javac
などのJavaコンパイラを介してデータベースにロードできます。
データベースに対して、loadjava
ツールで管理されるロード・プロセス中にソースをコンパイルするように指定できます。
この項の内容は、次のとおりです。
javac
など、従来のJavaコンパイラを使用してJavaソース・コードをコンパイルできます。コンパイルが終了した後は、ソースではなく、コンパイル済バイナリをデータベースにロードします。通常は、データベースでデバッグするよりも、各自のシステムでJavaコードをデバッグする方が簡単なため、これは便利なオプションです。
ソース・ファイルのloadjava
ツールに-resolve
オプションを指定すると、次の処理が実行されます。
ソース・ファイルがソース・スキーマ・オブジェクトとしてロードされます。
ソース・ファイルがコンパイルされます。
コンパイル済の.java
ファイルに定義されている各クラスに対して、クラス・スキーマ・オブジェクトが作成されます。
コンパイル済のコードがクラス・スキーマ・オブジェクトに格納されます。
Oracle Databaseによって、すべてのコンパイル・エラーがloadjava
ツールのログ・ファイルとUSER_ERRORS
ビューに書き込まれます。
-resolve
オプションを指定せずにデータベースにJavaソースをロードすると、実行時にクラスが必要になったときに、Oracle Databaseによってソースが自動的にコンパイルされます。ソース・ファイルは、ソース・スキーマ・オブジェクトにロードされます。
Oracle Databaseによって、すべてのコンパイル・エラーがloadjava
ツールのログ・ファイルとUSER_ERRORS
ビューに書き込まれます。
コンパイラ・オプションを指定するには、次の方法があります。
loadjava
ツールのコマンドラインにコンパイラ・オプションを指定します。loadjava
ツールには、encodingオプションを指定することもできます。
JAVA$OPTIONS
表に永続コンパイラ・オプションを指定します。JAVA$OPTIONS
表はスキーマごとに存在します。コンパイルする度に、これらのオプションが使用されます。ただし、loadjava
ツールにコンパイラ・オプションを指定すると、この表に定義されているオプションはオーバーライドされます。この方法でコンパイラ・オプションを指定する場合は、この表を独自に作成する必要があります。
デフォルトのコンパイラ・オプション
コンパイル対象のソース・スキーマ・オブジェクトに、JAVA$OPTIONS
表のエントリもコマンドラインのオプション値も指定されていない場合は、次のデフォルト値が使用されます。
このオプションは、SQLJ構文が含まれているJavaソースにのみ適用されます。
このオプションは、次のように指定した場合と同じです。
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ソース・スキーマ・オブジェクトのコンパイルに使用するオプションが |
option |
|
最初、スキーマには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 |
|
|
|
a.b |
|
|
|
空文字列 |
|
|
|
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() } }
システムは詳細なクラス・レベルで依存関係を追跡します。この例では、A
がB
のインスタンスを保持し、B
がC
のインスタンスを保持し、C
がA
のインスタンスを保持しているため、クラスA
、B
およびC
が相互に依存しています。クラスA
に新しいフィールドを追加して、このクラスの定義を変更すると、Oracle Databaseの依存関係メカニズムによって、クラスB
とC
が無効になったことを示すフラグが設定されます。これらのクラスを次に使用する前に、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
スキーマにロードされます。あるクラスが、事前に定義した他のクラスに依存している場合は、すべてのクラスをスキーマにロードすることになります。たとえば、スキーマが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
オプションを使用して、定義されたスキーマ内で検索するオブジェクトを指定します。前述の例では、SCOTT
、OTHER
およびPUBLIC
で、すべてのクラス・スキーマ・オブジェクトが検索されます。スキーマで特定のクラスまたはクラス・グループのみを検索する場合は、検索範囲を絞り込むことができます。たとえば、OTHER
スキーマのmy/gui/*
クラスのみを検索するには、次のようにリゾルバ仕様を定義します。
loadjava -resolve -resolver '((* SCOTT) ("my/gui/*" OTHER) (* PUBLIC))'
リゾルバ仕様の最初のパラメータには、クラス・スキーマ・オブジェクトを指定し、2番目のパラメータにはこれらのクラス・スキーマ・オブジェクトを検索するスキーマを定義します。
存在しないクラスへの参照の許可
リゾルバ仕様には、存在しないクラスへの未解決の参照を可能にする特別なオプションを指定できます。製品では内部クラスがまったく使用されないこともあります。標準のJava環境では、JVMはメソッドがコールされないかぎりメソッドを無視するため、問題は起こりません。ただし、クラスを解決するとき、Oracle JVMは使用できない名前も含め、そのクラスが参照しているすべての名前を解決しようとします。そのクラスが参照しているそれぞれの名前に対して一致するクラスをOracle JVMが見つけることができない場合、解決中のクラスは無効とマークされ、実行できません。
参照を無視するには、リゾルバ仕様でワイルドカードのマイナス記号(-
)を指定します。次の例は、参照先のクラスがリゾルバ仕様のスキーマ・リストに存在しない場合にも、my/gui
内のクラスへのすべての参照を許可するように指定しています。
loadjava -resolve -resolver '((* SCOTT) (* PUBLIC) ("my/gui/*" -))'
ワイルドカードを指定しないと、あるスキーマで依存クラスが見つからないと、そのクラスは無効としてリストされ、実行できなくなります。
また、検索できなかったクラスをすべて無視するように定義することもできます。ただし、依存クラスが見つからなかった場合にも、その依存クラスを必要としているクラスが有効としてマークされる可能性があるため、この方法は危険です。ただし、依存クラスを必要としているクラスは、依存クラスなしでは実行できません。この場合は、実行時に例外となります。
SCOTT
またはPUBLIC
に存在しないクラスをすべて無視するには、次のリゾルバ仕様を指定します。
loadjava -resolve -resolver "((* SCOTT) (* PUBLIC) (* -))"
後でロードするクラスがマイナス記号(-)ワイルドカードを含むリゾルバを必要とするような場合は、最初からそのようなリゾルバを使用しないでください。かわりに、参照先クラスをすべてスキーマに取り込んでから、解決してください。
マイナス記号(-)ワイルドカードを使用する場合でも、クラスのスーパー・クラスは存在する可能性ガあります。スーパー・クラスが見つからない場合、リゾルバでマイナス記号(-)ワイルドカードを使用しているかどうかに関係なく、そのクラスは無効になります。
注意: 存在しないクラスを処理する代替のメカニズムには、loadjava ツールの-gemissing オプションを使用します。このオプションを使用すると、loadjava ツールによって、参照される未定義のクラス定義が作成され、ロードされます。 |
JVMの仕様では、定義したクラスをJVMで使用するには、その前に.class
ファイルを検証する必要があります。Oracle JVMでは、クラスの解決時に検証が実行されます。
表2-4に、リゾルバで検出される問題と、発行される該当のOracleエラー・コードを示します。
表2-4 ORAエラー
エラー・コード | 説明 |
---|---|
|
リゾルバでクラスの形式が不正と判断されると、そのクラスは有効とマークされません。リゾルバでクラスが拒否されると、 マイナス記号(-)ワイルドカード式をリゾルバで使用し、一部のクラスの有効性を検証していない場合も、 |
|
状況によっては実行時に例外をスローできるように、リゾルバでは、有効とマークしたクラスのバイトコードが変更されることがあります。このような場合、リゾルバは |
マイナス記号(-
)ワイルドカードが指定されているリゾルバは、参照先クラスの有無に関係なく、クラスを有効とマークします。継承とインタフェースのために、あるクラスのインスタンスをスーパークラスまたは特定のインタフェースのインスタンスであるかのように使用する有効なJavaメソッドの作成が必要になる場合があります。検証対象のメソッドがクラスA
への参照をクラスB
への参照であるかのように使用している場合、リゾルバはA
がB
を拡張または実装しているかどうかを確認します。たとえば、次のメソッドについて考えてみます。シグネチャでは暗黙的にB
のインスタンスを戻すことになっていますが、そのボディはA
のインスタンスを戻します。
B myMethod(A a) { return a; }
このメソッドは、A
がクラスB
を拡張する場合、またはA
がインタフェースB
を実装している場合のみ有効です。A
またはB
の解決にマイナス記号(-)ワイルドカードが使用された場合、リゾルバはこのメソッドの安全を判断できません。この場合、myMethod
がコールされると、myMethod
のバイトコードは例外をスローするバイトコードに置換されます。
A
とB
のクラス定義が明確に識別されるスキーマにこれらがある場合、マイナス記号(-)
ワイルドカードがないリゾルバは、そのクラス定義を検索して適切に解決します。代替リゾルバの使用は、JARファイルにない他の非システム・クラスを参照する既存のJARファイルのロードが必要な場合にのみ検討します。
この項では、loadjava
ツールを使用してデータベースにクラスをロードする概要について説明します。loadjava
ツールを使用すると、ファイルからスキーマ・オブジェクトを生成して別のスキーマにロードできます。次に例を示します。
loadjava -u scott -schema TEST MyClass.java
Password: password
注意: コマンドライン・インタフェースを使用する場合、クラスをスキーマ・オブジェクトとしてデータベースにロードする必要はありません。次に例を示します。
C:\oraclehome\bin>loadjava -u scott MyClass.java
Password: password
コマンドライン・インタフェースの詳細は、「コマンドライン・インタフェースの使用」を参照してください。 |
loadjava
ツールは、SQLコマンド内から実行することもできます。ファイルからコンパイルおよびロードする従来のJVMとは異なり、Oracle JVMはデータベース・スキーマ・オブジェクトからコンパイルおよびロードします。
表2-5に、従来のJVMで使用されるファイルに対応するデータベース・スキーマ・オブジェクトを示します。
すべてのクラスやリソースは、データベースにロードして、そのデータベース内の他のクラスで使用できるようにする必要があります。さらに、ロード時にはデータベース内のクラスを実行できるユーザーを定義します。
表2-6に、loadjava
ツールが各タイプのファイルに対して実行するアクティビティを示します。
dropjava
ツールは、loadjava
ツールとは逆の動作を実行します。Javaファイルに対応するスキーマ・オブジェクトを削除します。loadjava
ツールで作成したJavaスキーマ・オブジェクトは、必ずdropjava
ツールを使用して削除します。次に例を示します。
dropjava -u SCOTT -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
をロードします。
クラスをロードするには、次のSQLデータベース権限が必要です。
スキーマにロードする場合はCREATE PROCEDURE
およびCREATE TABLE
権限。
別のスキーマにロードする場合はCREATE ANY PROCEDURE
およびCREATE ANY TABLE
権限。
oracle.aurora.security.JServerPermission.loadLibraryInClass.
classname
。
loadjava
ツールでは、.class
、.java
、.properties
、.sqlj
、.ser
、.jar
または.zip
ファイルを使用できます。JARファイルまたはZIPファイルには、ソース・ファイル、クラス・ファイルおよびデータファイルを挿入できます。loadjava
ツールにJARファイルまたはZIPファイルを渡すと、それはアーカイブをオープンし、そのメンバーを個別にロードします。JARまたはZIPのスキーマ・オブジェクトはありません。JARまたはZIPの内容が前回のロード時から変更されていない場合は、再ロードされません。したがって、JARファイルまたはZIPファイルのロードによるパフォーマンス低下は、ほとんどありません。実際には、JARファイルまたはZIPファイルのロードが、loadjava
ツールを使用する最も簡単な方法です。
データベース常駐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
オプションを使用します。
次のコマンドを使用します。
SQL> grant execute on myclass to scott;
myclass
は、基礎となるJavaクラスの名前です。
クラス名を短縮する場合は、次のコマンドを使用できます。
SQL> select dbms_java.shortname('long classname') from dual;
注意: Oracle Database 11gリリース1(11.1)以前では、ストアド・プロシージャの実行権限を付与する場合、ストアド・プロシージャと、そのストアド・プロシージャによって参照されるJavaクラスの両方の実行権限が付与されます。Oracle Database 11gリリース以降では、基礎となるJavaクラスの実行権限も付与する場合、そのクラスに対する実行権限を明示的に付与する必要があります。これは、セキュリティの向上を目的として実装されています。 |
図2-3は、クラスを実行するために必要な権限を示しています。
PL/SQLコードの実行中は、常に現行ユーザーが存在します。Javaコードの実行にも同じ概念が使用されます。まず、現行ユーザーは、Javaコードを起動するセッションを作成するユーザーです。Javaメソッドは、対応するラッパーによってSQLまたはPL/SQLからコールされます。Javaラッパーは、JavaメソッドをPL/SQLストアド・プロシージャまたはファンクションとしてSQLおよびPL/SQLに公開する特殊なPL/SQLエンティティです。このようなラッパーは、有効な現行ユーザーを変更できます。有効な現行ユーザーをラッパーの所有者に変更するラッパーは、定義者権限ラッパーと呼ばれます。ラッパーが有効な現行ユーザーを変更しない場合、有効なユーザーはそのまま変わりません。
Javaラッパーは、デフォルトで定義者権限ラッパーです。これをオーバーライドするには、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
表を問合せできます。
実行者権限のデフォルト動作のオーバーライドが必要になる場合もあります。本社がanalyze
プロシージャを使用して販売手数料を算出し、中央のpayroll
表を更新する場合を考えます。analyze
の実行者が従業員の給料や他の機密性の高いデータが格納されているpayroll
表に直接アクセスすることは問題です。
図2-5に示すように、analyze
プロシージャから定義者権限プロシージャcalcComm
をコールし、このプロシージャでpayroll
表を更新することで解決します。
USER_OBJECTS
データベース・ビューを問い合せることで、Javaソース、クラスおよびリソースを含め、独自に所有しているスキーマ・オブジェクトに関する情報を取得できます。たとえば、ロードしたソース、クラスまたはリソースがスキーマ・オブジェクトに正しく格納されているかどうかを確認できます。
表2-7に、USER_OBJECTS
の主な列とその説明を示します。
表2-7 主なUSER_OBJECT列
名前 | 説明 |
---|---|
|
オブジェクトの名前。 |
|
|
|
オブジェクトのステータス。値は、 |
オブジェクト名とオブジェクト・タイプ
USER_OBJECTS
のOBJECT_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
です。
次の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 10gリリース2 (10.2)およびOracle Database 11gでは、Javaアクティビティを簡単かつ直接的に監査する監査オプションが用意されています。また、Javaソース、クラスおよびリソースの変更も監査できます。
Javaスキーマ・オブジェクトに関連するデータベース・アクティビティを、文レベルおよびオブジェクト・レベルの2種類のレベルで監査できます。文レベルでは、特定パターンの文に関連したすべてのアクティビティを監査できます。
表2-8に、Javaスキーマ・オブジェクトに関連する文監査オプションと対応するSQL文を示します。
表2-8 Javaスキーマ・オブジェクトに関連する文監査オプション
文のオプション | SQL文 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
たとえば、ALTER JAVA SOURCE
のDDL文を監査する場合は、SQLプロンプトに次の文を入力します。
AUDIT ALTER JAVA SOURCE
オブジェクト・レベルの監査では、より詳細な監査が可能です。これは、特定のオブジェクトをクローズアップすることで、特定の問題を識別できます。
表2-9に、各Javaスキーマ・オブジェクトごとのオブジェクト監査オプションを示しています。セル内のエントリXは、そのJavaスキーマ・オブジェクトについて、対応するSQLコマンドを監査できることを示します。エントリNAは、そのJavaスキーマ・オブジェクトについて対応するSQLコマンドが適用不可であることを示します。
表2-9 Javaスキーマ・オプションに関連するオブジェクト監査オプション
オブジェクト・オプション | Javaソース | Javaリソース | Javaクラス |
---|---|---|---|
|
X |
NA |
X |
|
NA |
NA |
X |
|
X |
X |
X |
|
X |
X |
X |
関連項目:
|
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に完全に準拠します。
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
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()
メソッドに対してクラスとそのクラスが常駐するスキーマを指定します。
スキーマ名とクラス名でオブジェクトをシリアライズします。
注意: システム・クラスがClass.forName() を起動すると、予期しない動作が発生することがあります。目的のクラスがSYS またはPUBLIC に常駐している場合のみ、そのクラスが検索されます。クラスがSYS またはPUBLIC のいずれにも常駐していない場合は、そのクラスに対してPUBLIC シノニムを宣言できます。 |
この項の内容は、次のとおりです。
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); ... }
検索するスキーマを特定できるリゾルバを指定することで、クラスを検索する位置の問題を解決できます。または、クラスがロードされているスキーマを指定できます。クラスのロード先スキーマが判明している場合は、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); }
スキーマ名とクラス名が入った1つのString
値をoracle.aurora.util.ClassForName.lookupClass()
メソッドに指定できます。このメソッドをコールすると、指定されたスキーマでそのクラスの位置が特定されます。文字列は次の形式で指定する必要があります。
"<schema>:<class>"
たとえば、スキーマSCOTT
でcom.package.myclass
の位置を特定するには、次のコードを使用します。
oracle.aurora.util.ClassForName.lookupClass("SCOTT:com.package.myclass");
注意: スキーマ名には大文字を使用してください。この場合、スキーマ名は大/小文字が区別されます。 |
クラスのシリアライズを解除すると、処理の一部として、名前に基づいてクラスが検索されます。検索が正常に行われるためには、シリアライズされたオブジェクトにクラス名とスキーマ名の両方が含まれている必要があります。
Oracle Databaseでは、オブジェクトをシリアライズまたはシリアライズを解除する際に、次のクラスを使用できます。
次に、クラスを参照するいくつかのメソッドの例を示します。
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セキュリティは、Java2セキュリティに準拠したシステム・リソースで施行されます。
オペレーティング・システム・リソースへのアクセスには、標準のコアJavaクラスとメソッドを使用できます。リソースにアクセスした後、そのリソースがアクティブである期間は、リソースの種類によって異なります。メモリーはガベージ・コレクションされます。専用モードのサーバーを使用している場合、ファイル、スレッドおよびソケットは、複数コールにわたって存続します。共有サーバー・モードでは、ファイル、スレッドおよびソケットはコール終了時に終了します。
メモリーがJavaオブジェクト・メモリーとオペレーティング・システムの構成メンバーの2つの領域に分割されているとします。Javaオブジェクト・メモリー領域にはすべてのオブジェクトと変数が含まれています。オペレーティング・システムの構成メンバーには、オペレーティング・システムが要求に応じてオブジェクトに割り当てるリソースがあります。これらのリソースにはファイルやソケットなどが含まれます。
基本的なプログラミング規則では、Javaオブジェクトとオペレーティング・システム構成メンバーの両方のメモリーをクローズするように規定されています。Javaプログラマは、ガベージ・コレクタによってメモリーが解放されると誤解することがあります。ガベージ・コレクタは、未使用のJavaオブジェクト・メモリーを収集するために作成されました。ただし、オペレーティング・システムの構成メンバーはクローズされません。オペレーティング・システムの構成メンバーは、Javaオブジェクトをガベージ・コレクションする前にプログラムでクローズする必要があります。
たとえば、オブジェクトがファイルをオープンするたびに、オペレーティング・システムはそのファイルを作成してオブジェクトにファイル・ハンドルを割り当てます。ファイルがクローズされないと、オペレーティング・システムはコールの終了またはJVMの終了までファイル・ハンドルの構成メンバーを保持します。そのため、これらの構成メンバーが不足する可能性があります。各オペレーティング・システムのハンドルは無限ではありません。ハンドルの不足を回避するには、メソッドを終了する前にリソースをクローズします。ソケットに連結されたストリームについてもソケットをクローズする前にクローズします。
パフォーマンス上の理由から、ガベージ・コレクタは各オブジェクトにハンドルがあるかどうかを確認できません。したがって、ガベージ・コレクタはJavaオブジェクトおよび変数を収集しますが、ハンドルを解放するための適切なオペレーティング・システム・メソッドは発行しません。
例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仮想マシンは、Java Standard Editionのjava.lang.Runtime.exec
メソッドのファミリーを完全にサポートしています。これらのメソッドによって、ユーザーが指定したコマンドを実行するための新しいオペレーティング・システム(OS)プロセスが生成されます。サーバーでは、これらのメソッドを慎重に使用する必要があります。Java仮想マシンでは、OSコマンドの実行許可はデフォルトですべてのデータベース・ユーザーには付与されず、権限のある管理者のみがこれを発行します。DBAは、「Oracle DatabaseでのRuntime.exec機能のセキュアな使用」の項を参照して、推奨事項に従ってください。また、これらの許可をデータベース・ユーザーに発行する際には慎重に行う必要があります。
この項の内容は、次のとおりです。
JMX(Java Management Extensions)は、アプリケーション、システム・オブジェクト、デバイス、サービス指向のネットワーク、JVM(Java仮想マシン)を管理および監視するツールを提供するJavaテクノロジです。このAPIを使用すると、そのクラスの動的な作成と変更が可能です。そのため、リソースが作成、インストールおよび実装されると、このテクノロジを使用してリソースを監視および管理できます。また、JMX APIにはリモート・アクセスが含まれているため、リモート管理プログラムはこのような目的で実行中のアプリケーションと対話できます。
JMXでは、特定のリソースは、MBean(マネージドBean)と呼ばれる1つ以上のJavaオブジェクトで構成されます。これらのMBeanは、管理エージェントとして機能するコア・マネージド・オブジェクト・サーバー(MBeanサーバーと呼ばれる)に登録され、Javaプログラミング言語に対応するほとんどのデバイスで実行できます。JMXエージェントは、MBeanが登録されるMBeanサーバーと、MBeanを処理するサービスのセットで構成されます。
Oracle Database 11gリリース1 (11.1)では、新規ロールJMXSERVER
と新規プロシージャdbms_java.start_jmx_agent
が導入され、データベースでJMXをサポートします。JMXSERVER
ロールには、セッションでMBeanServerとJMXエージェントを起動および実行できる特定のJavaパーミッションが付与されます。プロシージャdbms_java.start_jmx_agent
は、特定のセッションで、セッションの期間内は通常アクティブのままのエージェントを起動します。JMXを有効にして起動するには、次の操作を実行します。
SYS
またはSYSTEM
からJMXSERVER
を取得します。
SQL> grant jmxserver to scott;
scott
はユーザー名です。
プロシージャ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プロパティは、Sun Java 5.0 JMXドキュメントで指定されたJavaプロパティのセットからのものです。Java JMXプロパティの一覧については、http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.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ストアド・プロシージャからプログラムでコールすることもできます。このメソッドは、通常、JMXエージェントを起動した後ですぐに終了します。したがって、次の文は、startOJVMAgent
メソッドが戻されると、すぐにJavaコールを終了させます。
call dbms_java.start_jmx_agent(...);
ただし、MBeanServerおよびJXMエージェントは初期化されたままの状態で、セッションの残りの存続期間は十分に機能します。Javaコールがスタック上に存在し、デーモン・スレッドによってJMXと接続されている場合のみ、Javaコードが特定のRDBMSセッションで実行されることに注意してください。したがって、2つの後続のJavaコール間の時間間隔においてJMXとクライアントとの対話は行われません。通常、JMXとの接続は各コールの終了時に停止し、次のコールの開始時に専用の(シャドウ)プロセスでは自動的に停止解除され、共有サーバー・プロセスではコールの終了時に完全に削除されます。デフォルトの専用プロセス構成では、デーモン・スレッドはコールとコールの間は休止状態になり、Javaコール間の時間間隔がクライアントとサーバーのタイムアウト間隔を超えなければ、頻繁にウェイクアップして同じソケット接続を透過的に再開できます。これにより、JConsoleなどのクライアントは、複数コールにわたって同じ物理接続を利用できます。共有サーバー・プロセスでは、ソケット接続はコールが終了しても維持されず、JConsoleなどのJMXクライアントは、次にコールするたびに再接続する必要があります。この接続の問題は、ソケット物理接続にのみ関係があります。両方のモードで、startOJVMAgent
メソッドはセッションのたびに1回のみ起動する必要があります。
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デモを参照してください。
dbms_java.start_jmx_agent
をアクティブにする場合、プロパティcom.sun.management.jmxremote
はtrue
に設定されます。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
です。このリソースのメカニズムは、Sun社によるファイルベースのJMX構成管理のOracle JVM拡張機能です。これは、セキュリティの向上とスキーマごとの管理を提供するため、Oracle JVMにとって優れたメカニズムです。リソースがスキーマに存在しない場合、ファイルの読取りはフォールバックとして行われます。com.sun.management.config.file
が設定されていない場合、デフォルトのファイル・パスは$(java.home)/lib/management/management.properties
です。Oracle Database 11gでは、このファイルに次の初期設定が含まれています。
com.sun.management.jmxremote.ssl.need.client.auth = true com.sun.management.jmxremote.authenticate = false
プロパティcom.sun.management.jmxremote.ssl.need.client.auth
とcom.sun.management.jmxremote.ssl
を併用すると、クライアントおよびサーバー証明書を使用する、双方向暗号化SSL認証のJMXが設定されます。com.sun.management.jmxremote.ssl
のデフォルト値はtrue
です。この構成はデフォルトであり、JAASパスワード認証よりも好まれます。
注意: パスワードのファイルベースの格納を提供するデフォルトのSun JMXログイン・モジュールは、セキュリティ上の理由からOracle JVMでサポートされていません脚注1。そのため、SSLクライアント認証のかわりにJAASパスワード認証を使用する必要がある場合、この項で示すように、一時的なJAAS資格証明をauth パラメータとしてdbms_java.start_jmx_agent に安全に渡すか、または安全なカスタムLDAPログイン・モジュールを使用するようにJMXを構成します。 |
脚注 1 JDK 5.0の以前のバージョンで特定されたRMI関連のJAASの脆弱性は、JDK 5.0_10で修正されており、11g Oracle JVMはこれに基づいています。ただし、クリアテキスト・ファイルを使用してパスワードを格納するデフォルトのJMXログイン・モジュールはサポートされません。パスワードを格納しないファイルjmxremote.access
は、サポートされています。
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.authenticate
はtrue
に設定されています。
JMXリスナーを、SSLやJAAS認証は使用せず、ポート9999で起動します。開発またはデモンストレーションの場合にのみ使用します。
call dbms_java.start_jmx_agent('9999', 'false', 'false');
この項では、JConsole(Sun社のJMXクライアント・ツール)を使用して、Oracle JVMを監視および制御する方法について説明します。JConsoleは、Sun Java JDKの構成要素です。
この項の内容は、次のとおりです。
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インタフェースは、次のタブで構成されています。
「Summary」タブ
Oracle JVMに関する概要情報と、JMXで監視される値が表示されます。詳細は、「Oracle JVMの概要情報の表示」を参照してください。
「Memory」タブ
メモリーの使用量に関する情報が表示されます。詳細は、「メモリー使用量の監視」を参照してください。
「Threads」タブ
スレッドの使用状況に関する情報が表示されます。詳細は、「スレッドの使用状況の監視」を参照してください。
「Classes」タブ
クラスのロードに関する情報が表示されます。詳細は、「クラスのロードの監視」を参照してください。
「MBeans」タブ
MBeanに関する情報が表示されます。詳細は、「MBeanの監視および管理」を参照してください。
「VM」タブ
Oracle JVMに関する情報が表示されます。詳細は、「VM情報の表示」を参照してください。
重要: Oracle Database 11gリリース2では、収集されて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との接続が成功すると、次の図のような「Summary」タブが表示されます。
表2-10で、「Summary」タブに表示されるフィールドについて説明します。
表2-10 JConsoleインタフェースの「Summary」タブのフィールドの説明
フィールド | 説明 |
---|---|
稼働時間 |
Oracle JVMセッションの実行時間。 |
プロセスCPU時間 |
Oracle Database 11gリリース2では、Oracle JVMセッションに関するこの情報は収集されません。 |
ライブ・スレッド |
有効なデーモン・スレッドと非デーモン・スレッドの現在の数。 |
ピーク |
Oracle JVMの起動以降の有効なスレッドの最大数。 |
デーモン・スレッド |
有効なデーモン・スレッドの現在の数。 |
開始合計 |
Oracle JVMを起動してから開始したスレッドの合計数。デーモン・スレッド、非デーモン・スレッド、終了したスレッドが含まれます。 |
現在のヒープ・サイズ |
現在ヒープで占められているKB数。 |
コミット済メモリー |
ヒープで使用するために割り当てられるメモリーの合計。 |
最大ヒープ・サイズ |
ヒープで占められる最大KB数。 |
ファイナライズを保留中のオブジェクト |
最終段階に向けて保留中のオブジェクトの数。 |
ガベージ・コレクタ情報 |
ガベージ・コレクタに関する情報。名前、実行したコレクションの数、ガベージ・コレクションの実行に費やす合計時間などです。 |
現在ロードされているクラス |
実行するために、現在メモリーにロードされているクラスの数。 |
ロードされているクラスの合計 |
セッションを開始してから、セッション・メモリーにロードされたクラスの合計数。 |
アンロードされているクラスの合計 |
メモリーからアンロードされたクラスの数。Oracle Database 11gリリース2の場合、通常はゼロになります。 |
合計物理メモリー |
Oracle Database 11gリリース2では、Oracle JVMセッションに関するこの情報は収集されません。そのため、表示される値はゼロになります。 |
空き物理メモリー |
Oracle Database 11gリリース2では、Oracle JVMセッションに関するこの情報は収集されません。そのため、表示される値はゼロになります。 |
コミット済仮想メモリー |
Oracle Database 11gリリース2では、Oracle JVMセッションに関するこの情報は収集されません。そのため、表示される値はゼロになります。 |
メモリー使用量の監視
JConsoleインタフェースの「Memory」タブを使用して、メモリー使用量を監視できます。このタブには、メモリー使用量とメモリー・プールに関する情報が表示されます。
図2-7に「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」タブを使用して、スレッドの使用状況を監視できます。
「Threads」タブのグラフには、有効なスレッドの数と時間が表示されます。特定のタイプのスレッドが特定の色で示されています。
マゼンタは、スレッドの合計数を表します。
赤は、スレッドの最大数を表します。
青は、有効なスレッドの数を表します。
このタブのスレッドのリストには、有効なスレッドが表示されます。リストのスレッドを選択すると、そのスレッドに関する情報が右側のペインに表示されます。スレッドの名前、状態、スタック・トレースなどの情報があります。
「Filter」フィールドは、スレッドの絞込みに役立ちます。
クラスのロードの監視
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では、属性CollectionUaageThreshold
およびUsageThreshold
が書込み可能です。
属性の値をダブルクリックすると、属性の値と時間を示すグラフも表示されます。たとえば、「GCManager」
MBeanの「CollectionTime」
プロパティの値をクリックすると、図2-11のようなグラフが表示されます。
属性をクリックすると、複雑な属性の詳細を表示できます。たとえば、図2-12に示すように、メモリー・プールの属性「Usage」
および「PeakUsage」
の詳細を表示できます。
MBeanの「Operations」タブには管理機能のインタフェースがあります。たとえば、「Perform Garbage Collection」をクリックすると、メモリー・プールまたはメモリー・マネージャに対してガベージ・コレクションを開始できます。Oracle JVMのJMXデモ(javavm/demo/jmx/
)には、Oracle JVMにロードされる追加のカスタムMBeanが複数用意されています。次の例で、「DBProps」
MbeanのgetProp
操作の結果を示します。
図2-13 JConsoleインタフェースの「MBeans」タブの「Operations」タブ
VM情報の表示
JConsoleインタフェースの「VM」タブを使用して、VM情報を表示できます。
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」タブを示します。
黒で表示されるパラメータは読取り専用のため、変更できません。たとえば、JavaPoolSize
、Platform
などです。青の値は読取り/書込みで、値が変更できることを表します。OracleRuntime
MBeanのほとんどの属性は、現在のセッションに対してローカルです。
OracleRuntime
MBeanのWholeJVM_
属性はグローバルです。これらの属性は、パフォーマンス・ビューv$session
およびv$sesstat
からの収集に従って、データベース・インスタンス内のすべてのJava対応セッションにおけるOracle JVMのメモリー使用量統計の合計を反映します。図2-16に、OracleRuntime
MBeanのWholeJVM_
属性を示します。
OracleRuntime
MBeanの「Operations」タブには、oracle.aurora.vm.OracleRuntime
クラスの操作の多くが公開されます。
さらに、図2-17と図2-18に示すように、sessionsRunningJava
操作およびsessionDetailsBySID
操作を使用して、Javaが有効な特定のデータベース・セッションの各メモリー使用量の統計を監視できます。
使用量のしきい値は、メモリー・プールの管理可能な属性です。コレクション使用量のしきい値は、ガベージ・コレクションされた一部のメモリー・プールの管理可能な属性です。プールの対応するしきい値のチェックを有効にするには、各属性を正の値に設定します。しきい値をゼロに設定すると、メモリー・プールのしきい値のチェックが無効になります。デフォルトでは、Oracle JVMのすべてのプールのしきい値のチェックは無効になっています。
使用量のしきい値とコレクション使用量のしきい値は、「MBeans」タブで設定されます。たとえば、左ペインのツリーからメモリー・プール「Old Generation」を選択し、このメモリー・プールの使用量のしきい値を20MBに設定して、コレクションのしきい値を1MBに設定した場合、しばらくすると、図2-19に示すように、しきい値交差イベントの数がカウントに表示されます。
メモリー・プール「Old Generation」のメモリー使用量が20MBを超えると、JConsoleインタフェースのメモリー・プール「Old Generation」を表すグラフの一部が赤に変わります。赤い部分は、使用済メモリーの一部が使用量のしきい値を超えたことを示します。図2-20に示すように、ヒープ・メモリーを表すグラフも赤に変わります。
図2-20 使用済メモリーが使用量のしきい値を超えた場合のJConsoleインタフェースの「Memory」タブ
SSLと認証を無効にしてリモート・リスナーを起動すると、一般的なセキュリティのガイドラインに違反するため、攻撃に対してサーバーが脆弱になります。そのため、本番環境ではこのようなモードを常に使用しないことをお薦めします。このモードは、JDKとの互換性や開発に対してサポートされます。Oracle JVMでJMXを本番で使用する場合は、安全なJMX接続を使用する必要があります。
セキュリティ関連のプロパティ値をdbms_java.set_property
、System.setProperty
またはdbms_java.start_jmx_agent
に指定する場合、非エコー・リスナーを使用するか、またはOracle Application Serverなどの安全なアプリケーション層から暗号化されたJDBC接続によってこれらを起動します。パスワードは、クリアテキスト・ファイルで保存しないでください。Oracleウォレットを使用して、証明書を作成および管理します。セキュリティを向上させるには、SSL認証のクライアント証明書を使用します。
関連項目: Oracleデータベースのセキュリティ機能の詳細は、『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では、標準のシステム・コールのかわりに非システム・ソリューションが採用されました。たとえば、システム・コールを使用せずにモニター同期ブロックやメソッドに入ることが可能です。
デッドロックを検出します。
シングル・スレッド・アプリケーションでは、次のイベントの発生でコールが終了します。
スレッドがコール元に戻された場合。
例外がスローされ、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() が実行された場合、共有および専用サーバー・プロセスの両方で、コールの終了前にすべてのスレッドが停止します。 |
注意: パフォーマンス上の理由から、専用サーバーをお薦めします。ただし、複数のコールでオープン状態のスレッドやソケットに依存するアプリケーションのクラスを使用することもできます。 |
共有サーバーを使用するセッションでは、複数のコールにわたる制限が存在します。これは、共有サーバーを使用するセッションは、後続のデータベース・コールによる同一プロセスへの接続が保証されていないためで、したがって、複数コールにわたって保持する必要があるセッション固有のメモリーおよびオブジェクトは、SGAに保存されます。つまり、スレッド、オープン・ファイルおよびソケットなどのプロセス固有のリソースは、各コールの終了時にクリーン・アップする必要があるため、次のコールでは使用できません。
この項の内容は、次のとおりです。
共有サーバー・モードの場合、Oracle Databaseでは、Javaプログラムの状態を次のコールまで保持するために、コール終了時にstatic
変数から参照可能なすべてのオブジェクトがセッション・スペースに移行されます。セッション・スペースはクライアントのセッション内にあり、複数コールにわたって存続するstatic
変数およびオブジェクトが格納されます。この移行処理は、各コールの終了時にOracle JVMで自動的に実行されます。
この移行処理にはメモリーが使用され、パフォーマンスにも影響します。したがって、複数のコールにわたって保持するstatic
変数やオブジェクトは最小限に抑えるように注意してください。static
変数にオブジェクトを不要に格納すると、メモリー・マネージャが移行処理を実行し、セッションごとにリソースを使用することになるため、不要な負担がかかります。static
変数を必要最低限に抑えることで、メモリー・マネージャの負担を軽減し、サーバーのパフォーマンスを向上させることができます。
Javaプログラムを同時に実行できるユーザー数を最大にするには、セッションのフットプリントを最小にすることが重要です。特に、拡張性を最大にするには、非アクティブなセッションが占有するメモリー領域を必要最小限にする必要があります。フットプリントを最小化する簡単な方法は、コール終了ごとに大きなデータ構造を解放することです。多くのデータ構造は、別のコールで再度必要になったときに遅延して再作成できます。このような理由から、Oracle JVMには、コール終了時など、セッションが非アクティブになる直前に、指定のJavaメソッドをコールするためのメカニズムがあります。
このメカニズムは、EndOfCallRegistry
通知と呼ばれます。これによって、コール終了時にstatic
変数を消去し、次のコールの着信時に遅廷初期化テクニックを使用して変数を再初期化できます。これは、複数コール間で保持するためにメモリー・マネージャが必要としている記憶域の容量に懸念がある場合のみ実行してください。Javaで実装する複雑でステートフルなサーバー・アプリケーションの場合のみ、懸念することになります。
コール終了時にデータ構造をNULLにリセットし、その後新しいコールごとにデータ構造を再作成するかどうかの決定は、時間と領域のトレードオフの問題です。データ構造の再作成によって処理時間は多少長くなりますが、次のコールまでデータ構造を保持する必要がないため、メモリー領域をかなり節約できます。また、セッション領域に移行されたオブジェクト(特に大型のオブジェクト)のアクセスには時間がかかり、効率が悪くなります。これは、セッションの表現がコール領域に基づくオブジェクトとは異なることに起因しています。
このような最適化の対象として、次のデータ構造があります。
バッファまたはキャッシュ。
静的フィールド(配列など)。これは、一度初期化するとプログラムの実行が完了するまで変化しません。
コール間の領域使用効率およびコール中の速度効率の向上が期待できる、動的に作成されるデータ構造。これによってコードが複雑になり、管理が困難になることもあります。したがって、労力に見合う程度の領域が節約される場合のみ、これの実施を検討してください。
コール終了時に消去する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() { ... } }
この例では、次の手順が実行されます。
static
フィールドthunk
にCallback
オブジェクトを作成します。
このCallback
オブジェクトをコール終了時の移行に登録します。
Callback.act()
メソッドを実装して、Callback
オブジェクト自体も含めて、すべてのstatic
変数を解放します。
キャッシュを遅延して再作成するための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() { ... } }
この例では、次の手順が実行されます。
インスタンスをCallback
オブジェクトとして実装します。
Callback.act()
メソッドを実装して、インスタンス・フィールドを解放します。
ユーザーがキャッシュを要求すると、Callback
オブジェクトによって、このオブジェクト自体がコール終了時の移行に登録されます。
キャッシュを遅延して再作成するためのcreateCachedField()
メソッドを指定します。
ユーザーがキャッシュを作成すると、Callback
オブジェクトがgetCachedField()
メソッドに自動的に登録されます。コール終了時に、Oracle JVMは、登録されたCallback.act()
メソッドをコールして、キャッシュを解放します。
この方法を使用すると、Callback
オブジェクトとそのインスタンスが同じオブジェクトになるため、オブジェクトの存続期間とそのインスタンスの存続期間が等しくなります。
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 パラメータが必要になります。ただし、大半のユーザーは、このパラメータの値を指定する必要はありません。 |
runCallbacks()
メソッドのシグネチャは、次のとおりです。
static void runCallbacks()
JVMはコール終了時にこのメソッドをコールし、registerCallback()
を使用して登録されたすべてのCallback
オブジェクトごとにact()
をコールします。コール終了時にこれがコールされた後で、オブジェクトが移行され、最終ステップが行われます。
注意: このメソッドは、作成したコード内ではコールしないでください。 |
インタフェースの宣言は、次のとおりです。
Interface oracle.aurora.memoryManager.Callback
EndOfCallRegistry.registerCallback()
を使用して登録するオブジェクトは、Callback
インタフェースを実装する必要があります。このインタフェースは、アプリケーションで、コール終了時に通知が必要な場合に役立ちます。
act()
メソッドのシグネチャは、次のとおりです。
public void act(Object value)
コールの終了時に実行する必要がある任意のアクティビティを実装できます。通常、このメソッドにはセッション・スペースに保存されるメモリーを消去するプロシージャが含まれます。
次の表に示すとおり、共有サーバー・モードでは、データベース・コールの終了時にOracle JVMが、オープン状態のオペレーティング・システム・リソースをすべてクローズします。
リソース | 存続期間 |
---|---|
ファイル | データベースのコールが終了すると、オープン状態のすべてのファイルがクローズされます。 |
スレッド | コールが終了すると、すべてのスレッドが終了します。 |
ソケット |
|
オペレーティング・システム・リソースに依存するオブジェクト | Javaオブジェクトの有効期間は、そのオブジェクトの使用可能期間に関係なく、セッションが存続している期間になります。たとえば、static クラス変数あるいはこのクラスを直接または間接的に参照するクラス変数にJavaオブジェクトを格納すると、セッション終了まで有効になります。使用可能期間の終了後にJavaオブジェクトを使用しようとすると、Oracle Databaseから例外がスローされます。これは次のような例に当てはまります。
|
1つのコールの終了時には、そのコールに対してローカルなリソースをクローズしてください。ただし、オペレーティング・システム・リソースを保持するstatic
オブジェクトについては、これらのリソースがコール終了後に受ける影響を考慮する必要があります。
ファイル
共有サーバー・モードでは、コールが終了すると、Oracle JVMはオープン状態のオペレーティング・システム構成メンバーを自動的にクローズします。これは、Javaオブジェクト内のオペレーティング・システム・リソースすべてに影響を与えます。static
変数の中でファイルをオープンした場合、そのファイル・ハンドルはコールの終了時にクローズされます。したがって、複数のコールにまたがってFile
オブジェクトを保持すると、次にファイル・ハンドルを使用するときに例外がスローされます。
例2-5のConcat
クラスは、複数のファイルを1つのファイルoutFile
に書き込むことができます。最初のコールでoutFile
が作成されます。最初の入力ファイルがオープンされ、読み取られてoutFile
に記録され、コールが終了します。outFile
はstatic
変数として定義されているため、次のコールが起動される間にセッション・スペースに移動します。ただし、ファイル・ハンドルは、コールが終了するとクローズします。次回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)で実行されます。
データベースに格納された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サーバーは適切に機能しません。RMIデーモン・スレッドを起動して共有サーバー・セッションでJMXを実行する場合、これらのデーモンが他のJMXに関連するデーモン・スレッドとともに自動的に再作成されます。デーモン・スレッドがJavaコールの開始時に再作成され、その後、そのコールがJMXエージェントを起動し、Javaコールの終了時にJMXエージェントも終了します。