Comments
Description
Transcript
1 - OTN - 日本オラクル
J2EEアプリケーションの移行にまつわる 問題 1 J2EEアプリ移植性にまつわる誤解 y 移行の経験のない技術者:両極端な移植性にまつわる認識 – 「コピーすれば動くんでしょ?」 y 確かにそういう場合もある。が、現実にはほとんどありえない – 「どうせ、(大幅な)コードの改変&リコンパイル無しに動くわけ無いんだから!」 y C言語とかを経てきた開発者にありがちな誤解 y そのアプリがAPサーバ固有の機能を利用しているのであれば誤解とはい えない 2 J2EEアプリ移植性:本来的な姿 APサーバ固有DD *.class java *.class java java*.class *.class *.jsp *.jsp *.jsp *.jsp *.jsp *.html, *.jpg, *.html, *.jpg, *.html, *.jpg, *.html, *.jpg, etc. etc. etc. etc. 変更なし 標準DD EAR, WAR, JAR web.xml application.xml application-client.xml ejb-jar.xml どのJ2EE サーバでも 動作します orionorionapplicationorion-ejborionweb.xml client.xml jar.xml application.x ml Oracle Application Server 10g weblogic.xml weblogic.xml weblogic.xml weblogic.xml WebLogic ibm-webibm-webibm-webbnd.xmi ibm-webibm-webbnd.xmi ibm-webbnd.xmi ibm-webbnd.xmi bnd.xmi bnd.xmi bnd.xmi WebSphere ここでAPサーバ依存部分 を吸収 others… DD: Deployment Descriptor (配布記述子) 3 コピーしただけで動く? y Oracle Application Serverはデプロイ時に固有DDを自動生成するので、動く可能性も ある – 現実には、(生成後に)固有DDをきちんと編集してもそれだけでは動かないケ ースがほとんど 自動生成 デプロイ *.class java *.class java java*.class *.class *.jsp *.jsp *.jsp *.jsp *.jsp *.html, *.jpg, *.html, *.jpg, *.html, *.jpg, *.html, *.jpg, etc. etc. etc. etc. orionweb.xml web.xml application.xml application-client.xml ejb-jar.xml orion-ejbjar.xml orionapplication.xml EARファイル orionapplicationclient.xml Oracle Application Server 10g 4 コピーしただけでは動かないのは? y y y y y J2SEのバージョンの違い J2EE仕様の実装の誤差 – 各ベンダによるJ2EE仕様の解釈に誤差がある J2EE仕様でオプション扱いの機能の実装の有無 APサーバ固有の機能 – J2EEに定められていない機能を各ベンダが独自に実装したものを利用している ため – 明示的に利用する場合と、知らず知らずに利用してしまっている場合がある APサーバ環境に依存するコーディング – ハードコードされたInitialContextに渡す環境変数、パスなど 5 なぜ仕様の解釈が食い違うのか? y 仕様はバージョンUPにより変更されるが、各APサーバ製品は上位互換性を維持する ために、過去の仕様に基づいたコードも動作させるようにする場合がある(確信犯的) – 開発者の利便性のために仕様を越えた実装をする場合もある – 本当はモード切替などで対応すべき(EJB1.1互換モード、とか) y 仕様が明確に定めていない部分がある 6 互換性のための意図的な実装例 y JSP 1.2仕様より – pageディレクティブのimportの仕様には次のように記述されている The default import list is java.lang.*, javax.servlet.*, javax.servlet.jsp.* and javax.servlet.http.*. y y この記述はJSP 1.1で追加されたものであり、1.0ではデフォルトのインポー トについては何の指定も無かった。つまり、各ベンダの判断に任されてい た JSP 1.0で java.util.* をデフォルトでインポートする実装にしたベンダ – そのAPサーバを利用して開発されたJSPでは java.util.* が暗黙的にインポート されていることが前提になってしまう – APサーバがJSP 1.1以降に対応する際に、java.util.* のデフォルトインポートを 外してしまうと、バージョンUPの際にそのままでは動かなくなるので、顧客の反 発を招くおそれがある – 顧客の過去の資産を守るために java.util.* をデフォルトインポートのままにす る… 7 ちなみに y Oracle Application Serverでは、古いJSP仕様に基づいたJSPをそのまま実行するため のモードが用意されている(global-web-application.xmlまたはorion-web.xmlに指定) – 暗黙的インポートの設定 extra_imports <init-param> <param-name>extra_imports</param-name> <param-value>java.util.* java.beans.*</param-value> </init-param> – pageディレクティブの重複宣言の許可(JSP 1.2からは、importを除くpageディレク ティブの重複宣言が禁止された) - forgive_dup_dir_attr 8 他にも… y JSPでの「charset=SJIS」 – <%@ page contentType=“text/html;charset=SJIS”%> という宣言がよく見受けら れる – JSP仕様のcontentTypeの説明には次の記述が… “CHARSET”, if present, must be the IANA name for a character encoding. – – SJISはIANA nameではない(charset=SJISの指定はOracle Application Serverで はエラーになる) ちなみに、 y 以前は charset=Shift_JIS という設定が広く使われていたが、現在、日本語 環境においては windows-31j を指定することが推奨されている。移行のタ イミングで charset=windows-31j と書き換えることも検討すべき 参考:http://www.iana.org/assignments/character-sets 9 仕様のあいまいさによる実装の食い違いの例 y Servlet仕様に定められているメソッドのひとつ、ServletContext.getRealPath y 次のように定められている public String getRealPath(String path) 与えられた仮想パスに対応する実際のパスを String オブジェクトで返します。例えば、“/index.html” というパスが与 えられたとするとサーバのファイルシステム上の絶対パスを返します。 そのパスは “http://host/contextPath/index.html” がリクエストされたときに提供するファイルのパスです。 ここで、contextPath という のはこの ServletContext のコンテキストパスを意味しています。 戻り値で得られる実際のパスは Servlet コンテナが動作しているコンピュータやオペレーティングシステムに合わせた 形式に変換されます。 パスセパレータも適切な形式になっています。このメソッドは Servlet コンテナが何かの理由(コ ンテンツが .war アーカイブで提供されているなど) で仮想パスを実際のパスに変換できなかった場合には null を返し ます。 パラメータ: path - 仮想パスを指定する String 戻り値: 実際のパスを表す String。 ただし、変換されなかった場合は null http://www.ingrid.org/jajakarta/servletapi/servletapi-4.0/docs-ja/javax/servlet/ServletContext.html getRealPath(java.lang.String) より抜粋 10 仕様のあいまいさ? y getRealPathメソッドのパラメータは仮想パスを示す文字列と定義されているが、仮想パ スを示す文字列としてどのようなものが有効であるかは定められていない – 例として “/index.html” が挙げられているのみである y しかし、この例が “/index.html” であって “index.html” でないことには注目 すべきである y 引数として指定される仮想パスとして、“index.html” あるいは “” (空文字) が有効かどうかについては仕様は何も触れていない – 仮に、そういった例外的な細かい部分を仕様に盛り込んでいくと、仕様は膨大 に膨れ上がってしまう。どうしてもあいまいさは残る。 11 あいまいさを突くコーディング y 次のようなコーディングが頻繁に見受けられる – String fstr = context.getRealPath(“”) + “asdf.txt”; y y 先に見たように、このようなコーディングは仕様では想定されていない もちろん、仕様の観点からは次のようにコーディングすべきである – String fstr = context.getRealPath(“/asdf.txt”); y このようなコーディングがどんな問題を引き起こすのか? – 5つの代表的なAPサーバ製品を用いて、getRealPathメソッドに4通りの引数を 与えてテストした結果を次にまとめた 12 getRealPathテスト結果(1) APサーバ Oracle Application Server 10g 引数(仮想パス) 戻り値(実パス) “/” C:¥OracleAS¥j2ee¥ … ¥applications¥test1¥GetRealPathTest¥ “/index.html” C:¥OracleAS¥j2ee¥ … ¥applications¥test1¥GetRealPathTest¥index.html “” C:¥OracleAS¥j2ee¥ … ¥applications¥test1¥GetRealPathTest “index.html” C:¥OracleAS¥j2ee¥ … ¥applications¥test1¥GetRealPathTestindex.html “/” D:¥JRun4¥servers¥default¥default-ear¥default-war¥ “/index.html” D:¥JRun4¥servers¥default¥default-ear¥default-war¥index.html “” D:¥JRun4¥servers¥default¥default-ear¥default-war¥ “index.html” D:¥JRun4¥servers¥default¥default-ear¥default-war¥index.html “/” D:¥tomcat41¥webapps¥ROOT¥ “/index.html” D:¥tomcat41¥webapps¥ROOT¥index.html “” D:¥tomcat41¥webapps¥ROOT “index.html” D:¥tomcat41¥webapps¥ROOT¥index.html A社製品 Tomcat 13 getRealPathテスト結果(2) APサーバ B社製品 引数(仮想パス) 戻り値(実パス) “/” D:¥IBM¥WebSphere¥AppServer¥ … ¥GalleryJA.war “/index.html” D:¥IBM¥WebSphere¥AppServer¥ … ¥GalleryJA.war¥index.html “” D:¥IBM¥WebSphere¥AppServer¥ … ¥GalleryJA.war¥ “index.html” D:¥IBM¥WebSphere¥AppServer¥ … ¥GalleryJA.war¥index.html “/” D:¥bea¥weblogic700¥ … ¥applications¥DefaultWebApp “/index.html” D:¥bea¥weblogic700¥ … ¥applications¥DefaultWebApp¥index.html “” D:¥bea¥weblogic700¥ … ¥applications¥DefaultWebApp “index.html” D:¥bea¥weblogic700¥ … ¥applications¥DefaultWebApp¥index.html C社製品 *すべてWindows版でテスト。別なプラットフォームでは別な結果になる可能性もある 14 getRealPathテストの総括 y y y コンテキスト・パスの直下に存在する asdf.txt というファイルの実パスを得るには、次の ようなコーディングが考えうるが、 – fstr = ctx.getRealPath( “/” ) + “asdf.txt”; – fstr = ctx.getRealPath( “/asdf.txt” ); – fstr = ctx.getRealPath( “” ) + “/asdf.txt”; – fstr = ctx.getRealPath( “” ) + “asdf.txt”; – fstr = ctx.getRealPath( “asdf.txt” ); 今回テストした5つのAPサーバのどれでも正しい実パスが得られる書き方は、2番目 のものだけである – fstr = ctx.getRealPath( “/asdf.txt” ); つまり、Servlet仕様で例示されている書き方のみがどのサーバでも正しい結果を出し た 15 でも… y J2EE全般にわたって正しく仕様を理解する、というのは一般的な開発者にとってはハー ドルが高いし、効率も悪い。現実的でない。 – 現実的なのは「動くように作る」ということ – 実際のところ、移植性が問題になるのは移行をするときである – 移行をすることによって、移植性を妨げるような問題のある作り方を学んでいく、 というのが現実的 16 移行の現場で起こりがちなその他の問題 y y APサーバ固有の日本語対応 JSP useBeanタグとJavaBeanのコンストラクタ y y y y クラスパス(クラスローダ)の問題 XMLパーサの問題 InitialContextをインスタンス化する際の環境変数 J2EE仕様から外れたEJB-QL y y イメージファイル等の名前の大文字・小文字の区別 INSERT/UPDATE/DELETE文をexecuteUpdateでなくexecuteQueryメソッドで投げて いる 17 APサーバ固有の日本語対応 y ブラウザから送られたフォームデータをWebコンテナ側で取り出す際の問題 … public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String var0 = ""; param0にて日本語の文字列を渡した try { 場合、このコードでは文字化けが発生 var0 = request.getParameter("param0"); するのがJ2EE標準の動作。 … 対応策1 … request.setCharacterEncoding("windows-31j"); try { var0 = request.getParameter("param0"); しかし、APサーバによってはWebコン テナに対してデフォルトのエンコーディ ングを指定する等の設定により、文字 化けが起きなくすることができる。 その動作はJ2EE標準ではないので、 このようなコードを移行する場合には 対応が必要と考えるべき … 対応策2 … var0 = new String(request.getParameter("param0").getBytes("8859_1"), "JISAutoDetect"); … 18 JSP useBeanタグとJavaBeanのコンストラクタ y Oracle Application ServerのJSP実装では、useBeanタグで利用されるJavaBeanに引数無 しのコンストラクタが存在することを前提とするため、当該JavaBeanに引数無しのコンス トラクタが存在しない場合、エラーとなる package com.stardeveloper.bean.test; public class SimpleBean implements java.io.Serializable { /* Properties */ private String name = null; private int age = 0; /* Empty Constructor */ public SimpleBean() {} /* Getter and Setter Methods */ public String getName() { return name; } public void setName(String s) { name = s; } public int getAge() { return age; } public void setAge(int i) { age = i; } JavaBeans are usual Java classes which adhere to certain coding conventions. Following are the coding conventions I am talking about : •Implements java.io.Serializable interface •Provides no argument constructor •Provides getter and setter methods for accessing it's properties http://www.stardeveloper.com/articles/display.html?article =2001071901&page=1 より抜粋 } 19 クラスパス(クラスローダ)の問題 y y OC4J(Oracle Application ServerのJ2EEコンテナ)のクラスローダの特性により問題が 発生する場合がある – 通常、ClassCastExceptionなどの形で問題が現れる – 詳細は、OTNの資料等を参照 y http://otndnld.oracle.co.jp/products/oc4j/pdf/902/ClassLoadingInOC4J_WP. pdf ライブラリが整理されていない環境において、クラスの走査の順番が移行前と移行先 の環境で異なってしまうために問題が発生する場合がある – 様々な形の問題を引き起こす – クラスパス上にバージョンの異なる同じクラスが存在する場合など 20 XMLパーサの問題 y OC4J(Oracle Application ServerのJ2EEコンテナ)はOracle XML ParserをXMLパーサ として内部的に利用する。J2EEアプリもデフォルトでOracle XML Parserを使用する。こ のXMLパーサが移行前の環境で利用されていたXMLパーサと微妙に異なる挙動を するために問題が生じる場合がある。 – 実際の移行の現場では、移行対象のJ2EEアプリがXerces、Xalanを利用してい るケースが非常に多い。そこで問題が発生した場合、OC4JでもXerces、Xalanを 利用する設定に切り替えることで対応することも可能 y 次の起動オプションを追加 -Xbootclasspath/a:d:¥xerces¥xerces.jar y OC4J FAQ (http://otndnld.oracle.co.jp/products/ias/htdocs/OC4J_FAQ_904/OC4JFAQ-904.html) の25番を参照 21 InitialContextをインスタンス化する際の環境変数 y javax.naming.InitialContextをインスタンス化する際に渡すプロパティをハードコーディ ングしている場合には、コードの変更が必要 … Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.ejs.ns.jndi.CNInitialContextFactory"); Context ic = new InitialContext(env); DataSource ds = (DataSource) ic.lookup("jdbc/DS_ORACLE"); … … Context ic = new InitialContext(); DataSource ds = (DataSource) ic.lookup("jdbc/DS_ORACLE"); … 22 J2EE仕様から外れたEJB-QL y y 現在、出荷中のAPサーバ製品はほとんどがJ2EE 1.3に対応したものであるが、ベンダ によっては、J2EE 1.4の機能を一部先取りの形で実装している そうとは知らずにJ2EE 1.4の機能を利用してしまっているケースも起こりうるが、そのよ うな場合、他のJ2EE 1.3対応APサーバでは動作しなくなってしまう <ejb-ql> SELECT OBJECT(o) FROM stock o WHERE stocktype LIKE ?1 ORDER BY location </ejb-ql> LIKE条件の中でのインプットパラ メータの使用や、ORDER BY、あ るいは集約関数などはEJB 2.0仕 様では定められていない 23 その他 y イメージファイル等の名前の大文字・小文字の区別 – OC4Jは大文字・小文字をきちんと区別するので、HTML内でHREF=で指定して いるイメージファイルのファイル名等が大文字・小文字含めて一致しないと、ブ ラウザに表示されない y INSERT/UPDATE/DELETE文をexecuteUpdateでなくexecuteQueryメソッドで投げて いる – 一部のAPサーバ付属のJDBCドライバではINSERT/UPDATE/DELETE文 をexecuteQueryで実行することができるようであるが、本来はexecuteQueryでク エリー文以外の文が投げられた際にはSQLExceptionを発行すべき 24 移行チェックシート 移行元と移行先のJ2SDK(JDK)のバージョンは同じである YES NO 移行元と移行先のAPサーバの対応するJ2EEのバージョンは同じである YES NO アプリケーションはEARまたはWARにアーカイブされている YES NO ソースコードにおいてAPサーバ依存のパッケージはimportしていない YES NO APサーバ固有の機能は使っていない(はずだ) YES NO J2EE仕様を正しく理解している(と思う) YES NO 移植性を保つようにコーディングした(つもり) YES NO すべてYESの場合、まずはOracleASにデプロイしてみる。APサーバ固有のDeployment Descriptorはデプロイ時に生成されるので、必要に応じて変更する ひとつでもNOがある場合、次に紹介するOracle JDeveloperを利用した移行手順を実施するの がお勧め すべてYESの場合でも、アプリを動かしてみて問題が出た場合には、やはり次に紹介する移行手 順を実施する 25 Oracle JDeveloperを利用した移行手順 1. 2. 3. 4. ディレクトリ構造を作成し、DD(Deployment Descriptor)とソースファイル類をコピー Oracle JDeveloperのプロジェクトを作成し、DDとソースファイル類をプロジェクトに 追加 ソースファイル類のチェック&修正 設定ファイル等のチェック&編集 z DDの変更、アプリケーション固有の設定ファイルの変更 5. 6. データベース構築&データベース接続を作成 埋め込みOC4Jを利用してテスト実行 7. デバッガを利用しての問題判別とコード修正 アーカイブ化し、デプロイ z 26 コードをチェック y y まずはビルドしてみて、どんなエラーが出るかをチェックするのが手っ取り早い – この段階で出るエラーは、クラスパスやライブラリの設定の不足によるものがほ とんど – APサーバ依存のパッケージの利用有無の判別 y インポートしていることが発覚した場合にはコードの修正が必要 Oracle JDeveloperのプロジェクト内検索の機能を利用して、特定のキーワードで文字列 検索をかける – InitialContext – ベンダ名、APサーバ製品名など、パッケージ名で使われそうな文字列 27 実行してみる y y アプリケーションが正常に動作するか確認 – Oracle JDeveloperに埋め込まれているJ2EEコンテナはOracle Application Serverに搭載されているJ2EEコンテナ(OC4J)と同じものであるため、 JDeveloperで正常に動作すればOracle Application Serverにデプロイしても同じ ように動作する 問題が発生した場合にはコードの修正をおこなう – 問題点の判別のためにOracle JDeveloperのデバッガ機能を活用する y 他のAPサーバでは正常に動いていたコードであるから、問題が発生した 場合には少々根の深いものであることが少なくない。デバッガを使うことで の早期の問題判別が重要 y 移行作業においては、デバッガを使いたいがためにOracle JDeveloperを利 用するといっても過言ではない 28 移行の工数は? y y y 移行そのものの工数に、アプリの規模はあまり関係ない – ただし、移行後の検証は別 なぜなら、ビジネス・ロジックが移行作業で問題になることはほとんどないから たいてい、問題になるのは「仕組み」つまり、システム的な部分、あるいは、フレームワ ーク的な部分である – 問題判別が難しいケースが多い。そのため、工数の見通しが立て辛いのも事 実 – Oracle JDeveloperのデバッガを使うことで早期に問題の核心に迫ることが重要 29 まとめ y y y 様々な要因によりJ2EEアプリケーションの移植性は損なわれる – APサーバ固有の機能を使わなければいいというものではない… しかし、別のAPサーバにアプリを移行することにより、移植性を高めていくことは可能 – そのプロセスを行うことで、J2EE仕様への理解を深めていくことにもなる Oracle JDeveloper 10gを利用した移行手順がお勧め 30 日本オラクル株式会社 無断転載を禁ず この文書はあくまでも参考資料であり、掲載されている情報は予告なしに変更されることが あります。日本オラクル社は本書の内容に関していかなる保証もいたしません。また、本書 の内容に関連したいかなる損害についても責任を負いかねます。 Oracleは米国Oracle Corporationの登録商標です。文中に参照されている各製品名及びサービス 名は米国Oracle Corporationの商標または登録商標です。その他の製品名及びサービス名はそれ ぞれの所有者の商標または登録商標の可能性があります。