以下、本実施の形態を図面を参照して説明する。
[第1の実施の形態]
第1の実施の形態を説明する。
図1は、第1の実施の形態の解析装置の例を説明する図である。
第1の実施の形態の解析装置10は、既存ソフトウェアによるデータベースクエリの発行を解析する。解析装置10をコンピュータや情報処理装置と言うこともできる。解析装置10は、クライアント装置でもよいしサーバ装置でもよい。
解析装置10は、記憶部11および処理部12を有する。記憶部11は、RAM(Random Access Memory)などの揮発性の半導体メモリでもよいし、HDD(Hard Disk Drive)やフラッシュメモリなどの不揮発性のストレージでもよい。処理部12は、例えば、CPU(Central Processing Unit)、GPU(Graphics Processing Unit)、DSP(Digital Signal Processor)などのプロセッサである。ただし、処理部12は、ASIC(Application Specific Integrated Circuit)やFPGA(Field Programmable Gate Array)などの特定用途の電子回路を含んでもよい。プロセッサは、RAMなどのメモリ(記憶部11でもよい)に記憶されたプログラムを実行する。複数のプロセッサの集合を「マルチプロセッサ」または単に「プロセッサ」と言うことがある。
記憶部11は、解析対象プログラム13を記憶する。解析対象プログラム13は、高水準言語で記述されたソースコードでもよいし、中間言語で記述されたバイトコードや低水準言語で記述されたオブジェクトコードなどコンパイル済みコードでもよい。解析対象プログラム13は、モジュール15,16,17などの複数のモジュールを含む。モジュールは、他のモジュールから呼び出すことができる単位プログラムである。モジュールは、関数や手続き(プロシージャ)やメソッドなどであってもよい。また、解析対象プログラム13は、データベース14を使用するプログラムである。
処理部12は、解析対象プログラム13を解析し、解析対象プログラム13によってデータベース14に対して生成され得るクエリと、解析対象プログラム13に含まれるモジュールのうち当該クエリの内容を決定しているモジュールとを判定する。例えば、データベース14は関係データベースであり、クエリはSQL文である。解析装置10による解析は、例えば、解析対象プログラム13を修正する際の影響範囲分析の一環として行う。この影響範囲分析の結果は、データベース14を介したモジュール間のデータ依存関係を示す。あるモジュールがクエリを発行してデータベース14にデータを格納し、他のモジュールがクエリを発行してデータベース14から当該データを抽出することがある。その場合、前者のモジュールが後者のモジュールに影響を与えている。
まず、処理部12は、解析対象プログラム13に含まれる複数のモジュールの間の呼び出し関係を検出する。呼び出し関係は、ソースコードやバイトコードの静的解析によって検出することが可能である。例えば、モジュール15がモジュール16から呼び出され、モジュール16がモジュール17から呼び出されるという呼び出し関係が検出される。
次に、処理部12は、解析対象プログラム13に含まれる複数のモジュールのうち1つのモジュール(第1のモジュール)を選択し、第1のモジュールを起動する。すなわち、処理部12は、第1のモジュールを起動点に設定し、第1のモジュールから処理が開始するように解析対象プログラム13を実行する。第1のモジュールから解析対象プログラム13が実行されるようにするため、例えば、第1のモジュールが受け付ける引数やグローバル変数に、空文字列や「0」やnullなどの所定の値を設定する。
選択する第1のモジュールは、クエリを受け付けてデータベース14に対してデータ操作を実行するデータベースアクセスプログラムに近い下流のモジュールであることが好ましい。データベースアクセスプログラムは、例えば、プログラミング言語が用意するデータベースアクセスAPI(Application Programming Interface)や、ORM(Object Relational Mapper)などのデータベース操作ライブラリなどである。選択する第1のモジュールは、データベースアクセスプログラムの呼び出し元であってもよい。例えば、処理部12は、モジュール15~17のうちモジュール15を選択して起動する。
次に、処理部12は、第1のモジュールの起動によってデータベース14に対して生成されたクエリを取得する。クエリの取得は、データベースアクセスプログラムが受け付けるクエリを監視することで行ってもよい。また、クエリの取得は、データベースアクセスプログラムを、受け付けたクエリを記録するダミープログラムに置換することで行ってもよい。処理部12は、取得したクエリが、SQLの構文などの所定のフォーマットを満たしているか判定する。例えば、取得したクエリが、データベース14に対するデータ操作の種類を示すコマンド名と、操作対象のテーブルを示すテーブル名とを少なくとも含んでいる場合、当該クエリが所定のフォーマットを満たすと判定される。コマンド名は、SELECT、INSERT、UPDATE、DELETEなどである。
取得したクエリが所定のフォーマットを満たしていない場合、処理部12は、解析対象プログラム13に含まれる複数のモジュールのうち、第1のモジュールを呼び出すことがある呼び出し元モジュール(第2のモジュール)を選択し、第2のモジュールを起動する。処理部12は、第1のモジュールの場合と同様、第2のモジュールの起動によってデータベース14に対して生成されたクエリを取得し、取得したクエリが所定のフォーマットを満たしているか判定する。このように、処理部12は、所定のフォーマットを満たすクエリが取得されるまで、起動するモジュールを呼び出し元に向かって遡る。
所定のフォーマットを満たすクエリが取得されると、処理部12は、呼び出し元のモジュールを選択して起動することを停止してよい。例えば、処理部12は、モジュール15から呼び出し関係を遡ってモジュール16を起動し、クエリ18を取得する。クエリ18が所定のフォーマットを満たすクエリである場合、処理部12は、モジュール16から呼び出し関係を遡ってモジュール17を起動しなくてよい。
そして、処理部12は、所定のフォーマットを満たすクエリと、当該クエリが生成されたときに起動点として設定されたモジュールとを示す解析結果19を生成して出力する。呼び出し関係を遡っている間に、所定のフォーマットを満たすクエリが初めて生成されたときの起動点は、クエリの内容を実質的に決定しているモジュールと言える。例えば、処理部12は、クエリ18とモジュール16とを対応付けた解析結果19を生成して出力する。処理部12は、解析結果19を表示装置に表示してもよい。
第1の実施の形態の解析装置10によれば、解析対象プログラム13に含まれる複数のモジュールの間の呼び出し関係が検出される。それらモジュールの間の1つのモジュールが起動され、データベース14に対して生成されたクエリが監視される。生成されたクエリが所定のフォーマットを満たしているか判定され、所定のフォーマットを満たすクエリが得られるまで、起動するモジュールが呼び出し元に向かって遡って選択される。これにより、解析対象プログラム13によって生成され得るクエリと、そのクエリの内容を実質的に決定しているモジュールとを効率的に判定することができる。
また、ソースコードのみを解析する静的解析方法と異なり、複数のモジュールが連携して動的にクエリが生成される場合であってもクエリを抽出することが可能となる。また、様々な引数を与えながら解析対象プログラム13の全体を試行錯誤的に実行する単純な動的解析方法と異なり、クエリの生成と無関係なモジュールの実行を抑制でき解析時間を短縮できる。また、データベースアクセスプログラムをダミープログラムに置換することで、データベース14を構築しなくてもクエリを抽出することが可能となる。
[第2の実施の形態]
次に、第2の実施の形態を説明する。
図2は、第2の実施の形態の解析装置のハードウェア例を示す図である。
第2の実施の形態の解析装置100は、データベースを利用する既存ソフトウェアを解析し、データベースに対して発行されるSQL文と当該SQL文の内容を決定するメソッドとの対応関係を示す解析結果を出力する。解析装置100をコンピュータや情報処理装置と言うこともできる。解析装置100は、ユーザが操作するクライアント装置でもよいし、複数のユーザにより共有されるサーバ装置でもよい。
解析装置100は、CPU101、RAM102、HDD103、画像インタフェース104、入力インタフェース105、媒体リーダ106および通信インタフェース107を有する。上記のユニットはバスに接続されている。解析装置100は、第1の実施の形態の解析装置10に対応する。CPU101は、第1の実施の形態の処理部12に対応する。RAM102またはHDD103は、第1の実施の形態の記憶部11に対応する。
CPU101は、プログラムの命令を実行する演算回路を含むプロセッサである。CPU101は、HDD103に記憶されたプログラムやデータの少なくとも一部をRAM102にロードし、プログラムを実行する。なお、CPU101は複数のプロセッサコアを備えてもよく、解析装置100は複数のプロセッサを備えてもよく、以下の処理を複数のプロセッサまたはプロセッサコアを用いて並列に実行してもよい。また、複数のプロセッサの集合を「マルチプロセッサ」または単に「プロセッサ」と言うことがある。
RAM102は、CPU101が実行するプログラムやCPU101が演算に用いるデータを一時的に記憶する揮発性の半導体メモリである。なお、解析装置100は、RAM以外の種類のメモリを備えてもよく、複数個のメモリを備えてもよい。
HDD103は、OS(Operating System)やアプリケーションソフトウェアなどのソフトウェアのプログラム、および、データを記憶する不揮発性の記憶装置である。なお、解析装置100は、フラッシュメモリやSSD(Solid State Drive)などの他の種類の記憶装置を備えてもよく、複数の不揮発性の記憶装置を備えてもよい。
画像インタフェース104は、CPU101からの命令に従って、解析装置100に接続された表示装置111に画像を出力する。表示装置111として、CRT(Cathode Ray Tube)ディスプレイ、液晶ディスプレイ(LCD:Liquid Crystal Display)、有機EL(OEL:Organic Electro-Luminescence)ディスプレイ、プロジェクタなど、任意の種類の表示装置を用いることができる。
入力インタフェース105は、解析装置100に接続された入力デバイス112から入力信号を取得し、CPU101に出力する。入力デバイス112として、マウス、タッチパネル、タッチパッド、キーボードなど、任意の種類の入力デバイスを用いることができる。また、解析装置100に複数の種類の入力デバイスが接続されていてもよい。
媒体リーダ106は、記録媒体113に記録されたプログラムやデータを読み取る読み取り装置である。記録媒体113として、例えば、磁気ディスク、光ディスク、光磁気ディスク(MO:Magneto-Optical disk)、半導体メモリなどを使用できる。磁気ディスクには、フレキシブルディスク(FD:Flexible Disk)やHDDが含まれる。光ディスクには、CD(Compact Disc)やDVD(Digital Versatile Disc)が含まれる。
媒体リーダ106は、例えば、記録媒体113から読み取ったプログラムやデータを、RAM102やHDD103などの他の記録媒体にコピーする。読み取られたプログラムは、例えば、CPU101によって実行される。なお、記録媒体113は可搬型記録媒体であってもよく、プログラムやデータの配布に用いられることがある。また、記録媒体113やHDD103を、コンピュータ読み取り可能な記録媒体と言うことがある。
通信インタフェース107は、ネットワーク114を介して他の情報処理装置と通信を行うインタフェースである。通信インタフェース107は、スイッチやルータなどの有線通信装置とケーブルで接続される有線通信インタフェースでもよいし、基地局やアクセスポイントと無線リンクで接続される無線通信インタフェースでもよい。
次に、業務ロジックの間の依存関係について説明する。
図3は、複数の業務ロジックの連携例を示す図である。
解析対象のアプリケーションソフトウェアは、複数の業務ロジックを含む。1つの業務ロジックは、1つの業務機能を実現するソフトウェア部品であり、他のメソッドから呼び出し可能なメソッドの集合である。複数の業務ロジックは、グローバル変数やデータベースなどのデータストアを介してデータ依存関係をもつことがある。
ある業務ロジックがグローバル変数に値を書き込み、他の業務ロジックが当該グローバル変数から値を読み出す場合、この2つの業務ロジックはデータ依存関係をもつ。前者の業務ロジックは後者の業務ロジックに影響を与えることがあるため、前者の業務ロジックを修正する場合には後者の業務ロジックの動作を確認することが好ましい。同様に、ある業務ロジックが関係データベースのテーブルにデータを書き込み、他の業務ロジックが当該テーブルからデータを読み出す場合、この2つの業務ロジックはデータ依存関係をもつ。前者の業務ロジックは後者の業務ロジックに影響を与えることがあるため、前者の業務ロジックを修正する場合には後者の業務ロジックの動作を確認することが好ましい。
一例として、データベース31を使用するアプリケーションソフトウェアを考える。データベース31は、複数のテーブルを含む関係データベースである。このアプリケーションソフトウェアは、業務ロジック32,33,34,35を含む。
業務ロジック32(業務ロジック#1)と業務ロジック33(業務ロジック#2)は、グローバル変数を共有する。よって、業務ロジック32と業務ロジック33は、グローバル変数を介して相互に影響を与える。すなわち、業務ロジック32の影響範囲に業務ロジック33が含まれ、業務ロジック33の影響範囲に業務ロジック32が含まれる。
また、業務ロジック32は、SQL文41を発行してデータベース31のテーブルBにデータを書き込むことがある。SQL文41は、テーブルBに新規レコードを挿入するINSERT命令を示すクエリである。業務ロジック34(業務ロジック#3)は、SQL文42を発行してデータベース31のテーブルBからデータを読み出すことがある。SQL文42は、テーブルBからレコードを検索するSELECT命令を示すクエリである。同様に、業務ロジック35(業務ロジック#4)は、SQL文を発行してデータベース31のテーブルBからデータを読み出すことがある。よって、業務ロジック32は、データベース31を介して業務ロジック34,35に影響を与える。すなわち、業務ロジック32の影響範囲に業務ロジック34,35が含まれる。
第2の実施の形態の解析装置100は、影響範囲分析の一環として、業務ロジック32,34,35のようなデータベース31を利用する業務ロジックから、発行され得るSQL文と当該SQL文の内容を実質的に決定しているメソッドとを検出する。
図4は、複数のメソッドの階層的呼び出し例を示す図である。
データベース31を利用する業務ロジックは、最終的にDBアクセッサ36にデータベース31のテーブル操作を依頼する。DBアクセッサ36は、SQL文を受け付け、受け付けたSQL文に従ってレコードの検索、挿入、更新、削除などのテーブル操作を実行するデータベースアクセスプログラムである。DBアクセッサ36は、プログラミング言語が用意するDBC(Database Connectivity)などデータベースアクセスAPIでもよいし、ORMなどのデータベースアクセスライブラリでもよい。
DBアクセッサ36を介してデータベース31にアクセスする業務ロジックは、様々なメソッドを階層的に呼び出す複雑な構造をもつことがある。1つの業務ロジックに含まれるメソッドには、データベースアクセスに関与するメソッドもあるし、データベースアクセスに関与しないメソッドもある。データベースアクセスを要する契機(アクセス要求)が発生すると、業務ロジックに含まれる一部のメソッドが階層的に呼び出され、一連のメソッド呼び出しを通じてSQL文が組み立てられる。すなわち、2以上のメソッドが連携してSQL文が組み立てられ、最終的にDBアクセッサ36が呼び出される。ただし、1つの業務ロジックにおいて引数に応じて異なる種類のSQL文が生成されることがある。例えば、1つの業務ロジックが、SELECT命令とINSERT命令の2種類のSQL文を生成することがある。また、生成されるSQL文の種類毎に、そのSQL文の内容を実質的に決定している支配的なメソッド(クエリ決定メソッド)が存在する。
一例として、メソッド51,52,53,54などの複数のメソッドを含む業務ロジック50を考える。メソッド51は、業務ロジック50の外部から呼び出されることがある始点メソッドである。メソッド51が呼び出されたことが、データベースアクセスの契機となることがある。メソッド51は、メソッド52を呼び出すことがある。メソッド52が実行されると、1以上の他のメソッドを経由してメソッド53が呼び出されることがある。メソッド53は、メソッド54を呼び出すことがある。メソッド54は、DBアクセッサ36を呼び出すことがある。よって、業務ロジック50の始点メソッドであるメソッド51が呼び出されると、業務ロジック50内の複数のメソッドが連鎖的に呼び出されてSQL文が組み立てられ、DBアクセッサ36にSQL文が渡されることがある。
第2の実施の形態では、ソースコードの静的解析のみでは抽出することが難しい動的に生成されるSQL文も抽出できるようにする。また、データベース31を実際に構築せずデータベース31の無い実行環境でもSQL文を抽出できるようにする。また、業務ロジックに含まれるメソッドの中からクエリ決定メソッドを効率的に検出できるようにする。
そこで、第2の実施の形態では、アプリケーションソフトウェアを実行して、DBアクセッサ36が呼び出される位置におけるDBアクセッサ36に渡されるべきSQL文を抽出するようにする。また、DBアクセッサ36を、受け付けたSQL文を記録するダミーDBアクセッサに置換して、データベース31への実際のアクセスが発生しないようにする。また、アプリケーションソフトウェアを実行する際の実行開始位置を始点メソッドとせず、DBアクセッサ36に近い下流のメソッドとすることで、データベースアクセスに関与しないメソッドが無駄に実行されることを抑制する。このとき、正しい構文のSQL文が生成されるまで、DBアクセッサ36に近い下流のメソッドから始点メソッドに近い上流のメソッドに向かって実行開始位置を変更していく。クエリ決定メソッドより下流のメソッドを実行開始位置に設定すると、SQL文の内容が不定になるため正しい構文のSQL文が生成されない。下流から上流に向かう間に最初に正しい構文のSQL文が生成されたときの実行開始位置が、クエリ決定メソッドであると推定される。
図5は、クエリ決定メソッドの例を示す図である。
データベース31を使用する業務ロジック60を解析することを考える。解析装置100は、DBアクセッサ36をダミーDBアクセッサ37に置換して業務ロジック60を実行する。ダミーDBアクセッサ37は、SQL文を受け付け、受け付けたSQL文に応じたデータベース31のテーブル操作を実行せず、受け付けたSQL文を記録するダミープログラムである。例えば、ダミーDBアクセッサ37は、テーブルBに新規レコードを挿入するINSERT命令を示すSQL文41を受け付け、受け付けたSQL文41を記録する。このとき、ダミーDBアクセッサ37は、データベース31に対して、テーブルBに新規レコードを挿入するテーブル操作を実行しない。
一例として、業務ロジック60は、メソッド61,62,63,64,65,66,67,68,69などの複数のメソッドを含む。メソッド61は、業務ロジック60の外部から呼び出し可能な始点メソッドである。メソッド61より下流のメソッドが調査対象となる。メソッド62,63,64は、DBアクセッサ36を直接呼び出すことがあるメソッドである。DBアクセッサ36がダミーDBアクセッサ37に置換された後は、メソッド62,63,64はダミーDBアクセッサ37を呼び出すことがある。
メソッド65は、メソッド62を呼び出すことがある。メソッド66は、メソッド63を呼び出すことがある。メソッド67は、メソッド64を呼び出すことがある。メソッド68は、メソッド66を呼び出すことがある。メソッド69は、メソッド67を呼び出すことがある。メソッド間の呼び出し関係は、ソースコードまたはバイトコードの静的解析によって検出できる。メソッド62,63,64は、実行開始位置を上流に向かって移動していくにあたって最初に実行開始位置とする基点メソッドである。メソッド68,69は、SQL文の内容を実質的に決定するクエリ決定メソッドである。
クエリ決定メソッドの探索では、解析装置100は、基点メソッドであるメソッド62を実行開始位置に設定して業務ロジック60を実行する。メソッド62を起動した場合にダミーDBアクセッサ37が正しい構文のSQL文を出力しないため、解析装置100は、実行開始位置を呼び出し元のメソッド65に変更して業務ロジック60を実行する。
同様に、解析装置100は、基点メソッドであるメソッド63を実行開始位置に設定して業務ロジック60を実行する。メソッド63を起動した場合にダミーDBアクセッサ37が正しい構文のSQL文を出力しないため、解析装置100は、実行開始位置を呼び出し元のメソッド66に変更して業務ロジック60を実行する。メソッド66を起動した場合もダミーDBアクセッサ37が正しい構文のSQL文を出力しないため、解析装置100は、実行開始位置を呼び出し元のメソッド68に変更して業務ロジック60を実行する。メソッド68を起動した場合にダミーDBアクセッサ37が正しい構文のSQL文を出力するため、解析装置100は、ダミーDBアクセッサ37が出力した当該SQL文について、メソッド68をクエリ決定メソッドであると判定する。
また、解析装置100は、基点メソッドであるメソッド64を実行開始位置に設定して業務ロジック60を実行する。メソッド64を起動した場合にダミーDBアクセッサ37が正しい構文のSQL文を出力しないため、解析装置100は、実行開始位置を呼び出し元のメソッド67に変更して業務ロジック60を実行する。メソッド67を起動した場合もダミーDBアクセッサ37が正しい構文のSQL文を出力しないため、解析装置100は、実行開始位置を呼び出し元のメソッド69に変更して業務ロジック60を実行する。メソッド69を起動した場合にダミーDBアクセッサ37が正しい構文のSQL文を出力するため、解析装置100は、ダミーDBアクセッサ37が出力した当該SQL文について、メソッド69をクエリ決定メソッドであると判定する。
図6は、クエリ決定メソッドの調査例を示す図である。
正しい構文のSQL文は、テーブル操作の種類を示すコマンド名と、操作対象テーブルを示すテーブル名とを少なくとも含むSQL文である。コマンド名は、SELECT、INSERT、UPDATE、DELETEなどである。カラム名を要するコマンドについては、正しい構文のSQL文は、コマンド名とテーブル名とカラム名を少なくとも含むSQL文である。一方、正しい構文のSQL文は、挿入するレコードのカラム値、更新後のレコードのカラム値、レコード検索条件であるWHERE句のカラム値など、具体的なカラム値を含まなくてよい。具体的なカラム値は、クエリ決定メソッドに対して適切な引数を与えればSQL文に設定されると考えられるためである。
例えば、メソッド63を実行開始位置に設定した場合、ダミーDBアクセッサ37はSQL文43を出力する。SQL文43は、空文字列である。よって、SQL文43は正しい構文のSQL文ではなく、メソッド63はクエリ決定メソッドではない。メソッド66を実行開始位置に設定した場合、ダミーDBアクセッサ37はSQL文44を出力する。SQL文44は、コマンド名「INSERT」を含むものの、テーブル名やカラム名を含まない。これは、メソッド66より上流のメソッドにおいて操作対象テーブルが決定されるためである。よって、SQL文44は正しい構文のSQL文ではなく、メソッド66はクエリ決定メソッドではない。メソッド68を実行開始位置に設定した場合、ダミーDBアクセッサ37はSQL文45を出力する。SQL文45は、コマンド名とテーブル名とカラム名を含み、具体的なカラム値を含まない。よって、SQL文45は正しい構文のSQL文であり、メソッド68はクエリ決定メソッドである。ただし、具体的なカラム値は、メソッド68の引数に依存している。
なお、業務ロジックを始点メソッド以外のメソッドから開始するにあたり、解析装置100は、実行開始位置のメソッドの引数に値を代入する。また、解析装置100は、実行開始位置のメソッドおよびそれより下流のメソッドにとってのグローバル変数(実行開始位置のメソッドより上流のメソッドのローカル変数を含む)に値を代入する。引数やグローバル変数に代入する値は、原則として空を示す値でよい。例えば、解析装置100は、数値型の引数やグローバル変数に「0」を代入し、文字列型の引数やグローバル変数に空文字列を代入し、オブジェクト型の引数やグローバル変数にnullを代入する。
ただし、解析装置100は、DBアクセッサを指定する変数には、ダミーDBアクセッサ37へのポインタを代入する。これにより、DBアクセッサ36に代えてダミーDBアクセッサ37が呼び出される。また、実行開始位置のメソッド内に条件分岐が存在する場合、解析装置100は、メソッド内の複数の実行経路(パス)の全てが試行されるように、分岐条件に使用される引数やグローバル変数の値を複数セット用意する。この場合、解析装置100は、引数やグローバル変数の値を変えながら、同一の実行開始位置のメソッドを繰り返し起動することになる。これは、実行されるパスによって呼び出し先メソッドが変わる可能性があるためである。条件分岐によって生じる複数のパスは、シンボリック実行などの静的解析方法によって抽出することができる。
次に、解析装置100の機能について説明する。
図7は、解析装置の機能例を示すブロック図である。
解析装置100は、ソースコード記憶部121、ライブラリ記憶部122、ソースコード解析部123、バイトコード解析部124、呼び出し関係記憶部125、メソッド探索部126、プログラム実行部127、解析結果記憶部128および解析結果表示部129を有する。ソースコード記憶部121、ライブラリ記憶部122および呼び出し関係記憶部125は、例えば、RAM102またはHDD103の記憶領域を用いて実現される。その他のユニットは、例えば、CPU101が実行するプログラムを用いて実現される。
ソースコード記憶部121は、解析対象のアプリケーションソフトウェアについて、高水準言語で記述されたソースコードを記憶する。また、ソースコード記憶部121は、アプリケーションソフトウェアが使用するライブラリのソースコードを入手可能である場合、ライブラリのソースコードを記憶する。ソースコードは、例えば、オブジェクト指向言語で記述されている。また、ソースコード記憶部121は、アプリケーションソフトウェアの設定を記述した外部ファイル(ライブラリやDBアクセッサの設定を記述した外部ファイルを含む)が存在する場合、外部ファイルを記憶する。ソースコード記憶部121に記憶されるソースコードや外部ファイルはユーザにより入力される。
ライブラリ記憶部122は、アプリケーションソフトウェアが使用するライブラリやDBアクセッサについて、中間言語で記述されたコンパイル済みのバイトコードを記憶する。また、ライブラリ記憶部122は、データベースにアクセスせずにSQL文を記録するダミーDBアクセッサについて、中間言語で記述されたコンパイル済みのバイトコードを記憶する。ただし、ライブラリ記憶部122は、バイトコードに代えて、低水準言語で記述されたコンパイル済みのオブジェクトコードを記憶してもよい。
ソースコード解析部123は、ソースコード記憶部121に記憶されたソースコードの静的解析を行い、ソースコードに記述された複数のメソッドの間の呼び出し関係を検出する。ソースコード解析部123は、検出された呼び出し関係を呼び出し関係記憶部125に記録する。また、ソースコード解析部123は、外部ファイルにメソッドが定義されている場合、外部ファイルのメソッドを呼び出し先として検出することがある。なお、ソースコード解析部123は、着目する業務ロジックのみ解析してもよい。
バイトコード解析部124は、ライブラリ記憶部122に記憶されたライブラリのバイトコードの静的解析を行い、ライブラリに含まれる複数のメソッドの間の呼び出し関係を検出する。ただし、ソースコードを入手可能なライブラリについてはバイトコード解析を行わなくてもよい。バイトコード解析部124は、検出された呼び出し関係を、ソースコード解析部123を介して呼び出し関係記憶部125に記録する。なお、バイトコード解析部124は、着目する業務ロジックに関連するライブラリのみ解析してもよい。
呼び出し関係記憶部125は、ソースコード解析部123により検出された呼び出し関係およびバイトコード解析部124により検出された呼び出し関係を示す呼び出し関係情報を記憶する。呼び出し関係情報は、メソッド呼び出し毎に、呼び出し元クラス、呼び出し元メソッド、呼び出し先クラスおよび呼び出し先メソッドを含む。
メソッド探索部126は、ユーザから始点メソッドの指定を受け付け、指定された始点メソッドより下流のメソッドの中からクエリ決定メソッドを探索する。クエリ決定メソッドの探索では、メソッド探索部126は、呼び出し関係記憶部125に記憶された呼び出し関係情報を参照して、始点メソッドから到達可能であってDBアクセッサを呼び出すことがあるメソッドを基点メソッドとして選択する。メソッド探索部126は、基点メソッドから始めて呼び出し関係を上流に向かって遡って、実行開始位置のメソッドを切り替えていく。メソッド探索部126は、実行開始位置のメソッドを選択する毎に、プログラム実行部127にアプリケーションソフトウェアの実行を依頼し、生成されたSQL文をプログラム実行部127から取得する。メソッド探索部126は、正しい構文のSQL文が得られるまで呼び出し関係を遡って、アプリケーションソフトウェアの実行を繰り返す。
メソッド探索部126は、正しい構文のSQL文が得られたときの実行開始位置のメソッドを、クエリ決定メソッドであると判定する。正しい構文のSQL文が得られると、メソッド探索部126は、生成されたSQL文と判定されたクエリ決定メソッドとを対応付けた解析結果を、解析結果記憶部128に格納する。
プログラム実行部127は、メソッド探索部126からの依頼に応じて、解析対象のアプリケーションソフトウェアを実行する。アプリケーションソフトウェアの実行では、プログラム実行部127は、ソースコード記憶部121に記憶されたソースコードおよび外部ファイルを読み出す。また、プログラム実行部127は、ライブラリ記憶部122に記憶されたライブラリおよびダミーDBアクセッサを読み出す。
プログラム実行部127は、メソッド探索部126から指定された実行開始位置のメソッドを呼び出すメインソースコードを生成し、メインソースコードを含むソースコード群をコンパイルする。このとき、プログラム実行部127は、実行開始位置のメソッドの引数と、実行開始位置のメソッドおよびそれより下流のメソッドのグローバル変数とを検出する。プログラム実行部127は、検出した引数およびグローバル変数に適切な初期値が代入されるように、メインソースコードを生成する。また、プログラム実行部127は、通常のDBアクセッサに代えてダミーDBアクセッサが呼び出されるように、メインソースコードを生成する。プログラム実行部127は、コンパイル済みコードを実行し、ダミーDBアクセッサが出力するSQL文をメソッド探索部126に通知する。
解析結果記憶部128は、メソッド探索部126が生成した解析結果を記憶する。解析結果は、SQL文とクエリ決定メソッドのメソッド名との組を1つ以上含む。
解析結果表示部129は、解析結果記憶部128に記憶された解析結果を表示装置111に表示する。なお、解析装置100は、プリンタなど表示装置111以外の出力デバイスに解析結果を出力してもよいし、他の情報処理装置に解析結果を送信してもよい。
図8は、ソースコードの例を示す図である。
ソースコード131は、ソースコード記憶部121に記憶されるアプリケーションソフトウェアのソースコードである。ソースコード131は、オブジェクト指向言語で記述されている。ソースコード131には、クラスTimeRecordRegistBeanが記述されている。クラスTimeRecordRegistBeanは、メソッドinsertを含む。メソッドinsertは、クラスTimeRecordDtoInterfaceのオブジェクトを引数dtoとして受け付ける。メソッドinsertを実行開始位置に設定する場合、解析装置100は、引数dtoにnullを代入することになる。メソッドinsertは、同じクラスのメソッドvalidate、クラスMospParamsのメソッドhasErrorMessage、同じクラスのcheckInsert、クラスTmdTimeRecordDaoのメソッドnextRecordId、クラスTmdTimeRecordDaoのメソッドinsertを呼び出すことがある。
図9は、呼び出し関係テーブルの例を示す図である。
呼び出し関係テーブル132は、呼び出し関係記憶部125に記憶される。呼び出し関係テーブル132には、ソースコード解析部123によるソースコード解析で検出された呼び出し関係が登録される。また、呼び出し関係テーブル132には、バイトコード解析部124によるバイトコード解析で検出された呼び出し関係も登録される。
呼び出し関係テーブル132は、呼び出し元クラス、呼び出し元メソッド、呼び出し先クラスおよび呼び出し先メソッドの項目を含む。呼び出し元クラスの項目には、メソッド呼び出しを行い得るメソッドが属するクラスのクラス名が登録される。呼び出し元メソッドの項目には、メソッド呼び出しを行い得るメソッドのメソッド名が登録される。このクラス名とメソッド名の組によって、メソッド呼び出しを行い得るメソッドが識別される。呼び出し先クラスの項目には、呼び出されるメソッドが属するクラスのクラス名が登録される。呼び出し先メソッドの項目には、呼び出されるメソッドのメソッド名が登録される。このクラス名とメソッド名の組によって、呼び出されるメソッドが識別される。
図10は、呼び出し関係グラフの例を示す図である。
呼び出し関係グラフ70は、メソッド間の呼び出し関係を示す有向グラフである。呼び出し関係グラフ70は、メソッドに対応するノードと呼び出し関係に対応する単方向エッジとを含む。あるノードから他のノードに向かうエッジは、前者のノードに対応するメソッドが後者のノードに対応するメソッドを呼び出すことがあることを意味する。
一例として、呼び出し関係グラフ70は、ノード71,72,73,74,75,76を含む。ノード71は、クラスTimeRecordRegistBeanのメソッドinsertを示す。ノード72は、クラスTimeRecordRegistBeanのメソッドvalidateを示す。ノード73は、クラスTmdTimeRecordDaoのメソッドinsertを示す。ノード74は、クラスTmdTimeRecordDaoのメソッドprepareStatementを示す。ノード75は、クラスTmdTimeRecordDaoのメソッドexecuteUpdateを示す。ノード76は、DBアクセッサを示す。
呼び出し関係グラフ70は、ノード71からノード72へのエッジと、ノード71からノード73へのエッジと、ノード73からノード74へのエッジと、ノード73からノード75へのエッジとを含む。また、呼び出し関係グラフ70は、ノード74からノード76へのエッジと、ノード75からノード76へのエッジとを含む。
よって、クラスTimeRecordRegistBeanのメソッドinsertは、クラスTimeRecordRegistBeanのメソッドvalidateを呼び出すことがあり、クラスTmdTimeRecordDaoのメソッドinsertを呼び出すことがある。クラスTmdTimeRecordDaoのメソッドinsertは、クラスTmdTimeRecordDaoのメソッドprepareStatementを呼び出すことがあり、クラスTmdTimeRecordDaoのメソッドexecuteUpdateを呼び出すことがある。クラスTmdTimeRecordDaoのメソッドprepareStatementは、DBアクセッサを呼び出すことがあり、クラスTmdTimeRecordDaoのメソッドexecuteUpdateは、DBアクセッサを呼び出すことがある。
クエリ決定メソッドの探索では、解析装置100は、DBアクセッサを呼び出すノード74のメソッドを基点メソッドと判断する。解析装置100は、ノード74のメソッドを実行開始位置として選択する。ノード74のメソッドを選択することで生成されるSQL文が正しい構文でない場合、解析装置100は、呼び出し元のノード73のメソッドを実行開始位置として選択する。ノード73のメソッドを選択することで生成されるSQL文が正しい構文である場合、解析装置100は、ノード73のメソッドをクエリ決定メソッドと判定し、ノード74が示す基点メソッドから始まる探索を終了する。
同様に、解析装置100は、DBアクセッサを呼び出すノード75のメソッドを基点メソッドと判断する。解析装置100は、ノード75のメソッドを実行開始位置として選択する。ノード75のメソッドを選択することで生成されるSQL文が正しい構文でない場合、解析装置100は、呼び出し元のノード73のメソッドを実行開始位置として選択する。ノード73のメソッドを選択することで生成されるSQL文が正しい構文である場合、解析装置100は、ノード73のメソッドをクエリ決定メソッドと判定し、ノード75が示す基点メソッドから始まる探索を終了する。
このように、ノード73のメソッドがクエリ決定メソッドであることは、ノード74が示す基点メソッドから呼び出し関係を遡った場合と、ノード75が示す基点メソッドから呼び出し関係を遡った場合の両方で判定される。一方で、SQL文とクエリ決定メソッドの対応情報は、この2つの場合で同一のものが得られる。そこで、解析装置100は、異なる基点メソッドから得られた対応情報を統合する。ここでは、ノード74が示す基点メソッドから呼び出し関係を遡った場合の対応情報と、ノード75が示す基点メソッドから呼び出し関係を遡った場合の対応情報とを、1つの対応情報に統合する。
図11は、SQL文の生成例を示す図である。
ソースコード133には、クラスTmdTimeRecordDaoが記述されている。クラスTmdTimeRecordDaoは、メソッドprepareStatementとメソッドinsertを含む。メソッドprepareStatementは前述のノード74に対応し、メソッドinsertは前述のノード73に対応する。メソッドinsertはメソッドprepareStatementを呼び出すことがある。メソッドprepareStatementは、文字列を引数sqlとして受け付ける。メソッドinsertは、インタフェースBaseDtoInterfaceの実装クラスのオブジェクトを引数baseDtoとして受け付ける。
メソッドprepareStatementを実行開始位置に設定した場合、SQL文46が生成される。SQL文46は空文字列である。メソッドprepareStatementは、SQL文を示す引数sqlを受け付けてDBアクセッサに転送しているため、引数sqlに空文字列が代入されるとSQL文46も空文字列になってしまう。よって、メソッドprepareStatementは、SQL文の内容を決定しておらずクエリ決定メソッドではない。一方、メソッドinsertを実行開始位置に設定した場合、SQL文47が生成される。SQL文47は、コマンド名「INSERT」とテーブル名「tmd_time_record」と11個のカラム名とを含む正しい構文のSQL文である。よって、メソッドinsertは、SQL文の内容を実質的に決定するクエリ決定メソッドである。なお、メソッドinsertを呼び出す際に引数baseDtoにnullが代入されるため、SQL文47は11個のカラムの具体的な値を含まない。
図12は、解析結果画面の表示例を示す図である。
解析結果表示部129は、解析結果画面80を生成して表示装置111に表示する。解析結果画面80は、入力フィールド81および解析結果テーブル82を含む。解析結果テーブル82は、解析結果記憶部128に記憶されている。
入力フィールド81には、始点メソッドのクラス名およびメソッド名がユーザにより入力される。ユーザから指定される始点メソッドは、解析対象の業務ロジックに含まれるメソッドのうち、その業務ロジックの外部から最初に呼び出されるメソッドである。指定された始点メソッドより下流のメソッド、すなわち、指定された始点メソッドとDBアクセッサとの間にあるメソッドの中から、クエリ決定メソッドが探索される。
解析結果テーブル82は、SQL文とメソッド名の項目を含む。SQL文の項目には、クエリ決定メソッドの探索の間にダミーDBアクセッサから出力された正しい構文のSQL文が登録される。メソッド名の項目には、検出されたクエリ決定メソッドのメソッド名が登録される。1つの始点メソッドの指定に対して、SQL文とメソッド名を対応付けたレコードが1つも得られないこともあるし、1つだけ得られることもあるし、2つ以上得られることもある。レコードが1つも得られないことは、指定された始点メソッドを起動してもデータベースアクセスが発生しないことを意味する。レコードが1つだけ得られたことは、指定された始点メソッドを起動すると1種類のデータベースアクセスが発生することを意味する。レコードが2つ以上得られたことは、指定された始点メソッドを起動すると、引数に応じて異なる種類のデータベースアクセスが発生することを意味する。
次に、解析装置100の処理手順について説明する。
図13は、呼び出し関係解析の手順例を示すフローチャートである。
(S10)バイトコード解析部124は、ライブラリ記憶部122に記憶されたライブラリのうち未解析のライブラリを1つ選択する。
(S11)バイトコード解析部124は、選択したライブラリのバイトコードを解析して、ライブラリに含まれるクラスおよびメソッドを検出し、メソッド間の呼び出し関係を検出する。バイトコード解析部124は、検出した呼び出し関係を呼び出し関係テーブル132に記録する。呼び出し関係は、呼び出し元クラス、呼び出し元メソッド、呼び出し先クラスおよび呼び出し先メソッドの組で特定される。
(S12)バイトコード解析部124は、ステップS10で未選択のライブラリがあるか判断する。未選択のライブラリがある場合はステップS10に進み、全てのライブラリを選択済みである場合はステップS13に進む。
(S13)ソースコード解析部123は、ソースコード記憶部121に記憶されたソースコードを解析し、ソースコードに記載されたメソッドを1つ選択する。
(S14)ソースコード解析部123は、ステップS13で選択したメソッドから呼び出されている呼び出し先メソッドをソースコードから抽出し、呼び出し関係を検出する。ソースコード解析部123は、検出した呼び出し関係を、呼び出し関係テーブル132に記録する。呼び出し関係は、呼び出し元クラス、呼び出し元メソッド、呼び出し先クラスおよび呼び出し先メソッドの組で特定される。
(S15)ソースコード解析部123は、ステップS14において呼び出し先メソッドとして動的実装メソッドが抽出されたか判断する。動的実装メソッドは、オブジェクト指向言語のインタフェースに属するメソッドや抽象クラスに属する抽象メソッドなど、メソッド名や引数の型は定義されているものの具体的な処理内容が定義されていないメソッドである。呼び出し先メソッドとして動的実装メソッドが抽出された場合はステップS16に進み、動的実装メソッドが抽出されなかった場合はステップS19に進む。
(S16)ソースコード解析部123は、抽出された動的実装メソッドに対応する具象メソッドがソースコードに含まれているか判断する。具象メソッドは、オブジェクト指向言語のインタフェースを実装する実装クラスのメソッドや抽象クラスを継承する具象クラスのメソッドなど、動的実装メソッドで未定義になっていた具体的な処理内容を補完したメソッドである。1つの動的実装メソッドに対して2以上の具象メソッドが定義されることがある。具象メソッドがソースコードに含まれる場合はステップS18に進み、具象メソッドがソースコードに含まれない場合はステップS17に進む。
(S17)ソースコード解析部123は、ソースコードと組み合わせて使用される外部ファイルに、抽出された動的実装メソッドに対応する具象メソッドが定義されているか判断する。外部ファイルに具象メソッドが定義されている場合はステップS18に進み、外部ファイルに具象メソッドが定義されていない場合はステップS19に進む。
(S18)ソースコード解析部123は、ソースコードまたは外部ファイルから具象メソッドを抽出し、具象メソッドを呼び出す呼び出し関係を呼び出し関係テーブル132に記録する。オブジェクト指向言語のインタフェースまたは抽象クラスを示す変数には、何れかの具象クラスのオブジェクトが代入されることになり、動的実装メソッドの呼び出しは実行時には何れかの具象メソッドの呼び出しに置き換えられる。このため、実行時の呼び出し関係を辿ることができるように、ステップS14で検出された呼び出し関係について、呼び出し元メソッドから具象メソッドへの呼び出し関係が追加される。
(S19)ソースコード解析部123は、ステップS13でソースコードに未調査のメソッドがあるか判断する。未調査のメソッドがある場合はステップS13に進み、全てのメソッドを調査済みである場合は呼び出し関係解析が終了する。
図14は、クエリ抽出の手順例を示すフローチャートである。
(S20)メソッド探索部126は、ユーザから始点メソッドの指定を受け付ける。
(S21)メソッド探索部126は、指定された始点メソッドの呼び出し先メソッドを呼び出し関係テーブル132から検索し、未探索リストに追加する。
(S22)メソッド探索部126は、未探索リストからメソッドを1つ選択する。
(S23)メソッド探索部126は、ステップS22で選択したメソッドが基点メソッドであるか判断する。例えば、メソッド探索部126は、選択したメソッドの呼び出し先クラスを呼び出し関係テーブル132から検索する。メソッド探索部126は、呼び出し先クラスにDBアクセッサが含まれる場合、選択したメソッドが基点メソッドであると判断し、呼び出し先クラスにDBアクセッサが含まれない場合、選択したメソッドが基点メソッドでないと判断する。選択したメソッドが基点メソッドである場合はステップS24に進み、基点メソッドでない場合はステップS25に進む。
(S24)メソッド探索部126は、選択した基点メソッドから呼び出し関係を遡ってクエリ決定メソッドを探索する。クエリ決定メソッド探索の詳細は後述する。
(S25)メソッド探索部126は、選択したメソッドの呼び出し先メソッドを呼び出し関係テーブル132から検索し、未探索リストに追加する。
(S26)メソッド探索部126は、未探索リストが空であるか判断する。未探索リストが空の場合はステップS27に進み、空でない場合はステップS22に進む。
(S27)メソッド探索部126は、ステップS24で記録されたSQL文とメソッド名の対応関係を示すテーブルを解析結果として出力する。すなわち、メソッド探索部126は、SQL文とメソッド名のテーブルを解析結果記憶部128に格納する。解析結果表示部129は、SQL文とメソッド名のテーブルを表示装置111に表示する。
図15は、クエリ決定メソッド探索の手順例を示すフローチャートである。
クエリ決定メソッド探索は、上記のステップS24で実行される。
(S30)メソッド探索部126は、基点メソッドを実行開始メソッドに指定する。
(S31)プログラム実行部127は、ソースコードに基づいて実行開始メソッドのシンボリック実行を行う。すなわち、プログラム実行部127は、実行開始メソッドの引数やグローバル変数に抽象的シンボルを割り当て、各変数の値を抽象的シンボルを用いて表現し、ソースコードに従って各変数の値の変化を追跡する。プログラム実行部127は、シンボリック実行によって実行開始メソッド内の1以上のパスを抽出し、各パスが選択される変数条件を判定する。分岐条件が引数やグローバル変数に依存する場合、引数やグローバル変数の値によって選択されるパスが変わる。
(S32)プログラム実行部127は、実行開始メソッドに与える引数や、実行開始メソッドおよびそれより下流のメソッドが参照するグローバル変数の初期値を決定する。原則として、数値型変数の初期値は「0」、文字列型変数の初期値は空文字、オブジェクト型変数の初期値はnullにするなど、データが存在しないことを示す初期値でよい。ただし、ステップS31で複数のパスが抽出された場合、プログラム実行部127は、それら複数のパスのうち未試行のパスを1つ選択し、選択したパスを通るように引数やグローバル変数の初期値を決定する。選択したパスを通るための初期値は、ステップS31で判定された変数条件を満たす初期値である。また、使用するDBアクセッサのオブジェクトを示すグローバル変数に、ダミーDBアクセッサのオブジェクトを代入することで、DBアクセッサをダミーDBアクセッサに置換できることがある。
(S33)プログラム実行部127は、実行開始メソッドを呼び出すメインソースコードを生成する。メインソースコードには、ステップS32で決定した初期値を引数やグローバル変数に代入する処理を記述する。また、メインソースコードには、DBアクセッサをダミーDBアクセッサに置換する処理を記述する。
(S34)プログラム実行部127は、元のアプリケーションソフトウェアのソースコードおよびステップS33で生成したメインソースコードをコンパイルして実行する。プログラム実行部127は、ダミーDBアクセッサが出力するSQL文を取得する。
(S35)メソッド探索部126は、ステップS34で正しい構文のSQL文が生成されたか判断する。正しい構文のSQL文は、SELECTやINSERTなどのコマンド名とテーブル名とを含み、カラムの指定を要するコマンドについては更にカラム名を含むSQL文である。コマンド名またはテーブル名が欠けたSQL文は、正しい構文のSQL文ではない。ただし、具体的なカラム値やWHERE句は含まなくてもよい。実行開始メソッドの起動により正しい構文のSQL文が生成された場合はステップS36に進み、正しい構文SQL文が生成されなかった場合はステップS37に進む。
(S36)メソッド探索部126は、今回の実行開始メソッドがクエリ決定メソッドであると判定する。メソッド探索部126は、ダミーDBアクセッサが出力したSQL文と今回の実行開始メソッドのメソッド名とを対応付けて記録する。
(S37)メソッド探索部126は、ステップS32の初期値の設定によって、実行開始メソッド内の全てのパスを試行したか判断する。全てのパスを試行した場合はステップS38に進み、未試行のパスが存在する場合はステップS32に進む。
(S38)メソッド探索部126は、今回の実行開始メソッドがクエリ決定メソッドであるか、すなわち、ステップS35の判断がYESであるか判断する。実行開始メソッドがクエリ決定メソッドである場合はステップS40に進み、実行開始メソッドがクエリ決定メソッドでない場合はステップS39に進む。
(S39)メソッド探索部126は、実行開始メソッドの呼び出し元メソッドを呼び出し関係テーブル132から検索する。呼び出し元メソッドが1つのみ存在する場合もあるし、呼び出し元メソッドが2つ以上存在する場合もある。
(S40)メソッド探索部126は、ステップS39で検索された呼び出し元メソッドのうち実行開始メソッドとして未実行の呼び出し元メソッドがあるか判断する。未実行の呼び出し元メソッドがある場合、未実行の呼び出し元メソッドのうちの1つを次の実行開始メソッドに指定してステップS31に進む。未実行の呼び出し元メソッドがない場合、クエリ決定メソッド探索が終了する。
第2の実施の形態の解析装置100によれば、ソースコードやバイトコードから静的解析によってメソッド間の呼び出し関係が検出される。ユーザから指定された始点メソッドより下流のメソッドのうちDBアクセッサを呼び出しているメソッドが基点メソッドとして選択され、DBアクセッサがダミーDBアクセッサに置換された上で、基点メソッドからアプリケーションソフトウェアが実行される。ダミーDBアクセッサが出力するSQL文が正しい構文のSQL文でない場合、正しい構文のSQL文が得られるまで、基点メソッドから呼び出し元に向かって呼び出し関係を遡って実行開始点が変更される。初めて正しい構文のSQL文が得られたときの実行開始点がクエリ決定メソッドと判定され、SQL文とクエリ決定メソッドとを対応付けた解析結果が出力される。
これにより、複数のメソッドが階層的に呼び出されて動的に生成されるSQL文も抽出することができる。また、始点メソッドを呼び出して業務ロジックの全体を試行錯誤的に実行する場合よりも、SQL文の生成と無関係なメソッドの実行を抑制でき解析時間を短縮できる。また、DBアクセッサをダミーDBアクセッサに置換することで、データベースを実際に構築しなくてもSQL文を抽出することが可能となる。また、DBアクセッサに近いメソッドから優先的に実行開始点に指定することで、SQL文毎にそのSQL文の内容を実質的に決定しているクエリ決定メソッドを効率的に探索できる。