Eclipseの最近のブログ記事
eclipseのプラグインを作っていて、このビルドをCIに乗せたい。
プラグインのエクスポート部分をどうやってantで実行したらいいのか、という話。
pde.exportPluginsタスクというのがあるので、これをbuild.xmlに書けばいいのだけども、このタスクが実行できるほどのCLASSPATHを通すことを考えるとめまいがしてしまう。
以下のページで、eclipseが抱えているant(org.eclipse.ant.core.antRunner)から実行する例が。
[news.eclipse.platform.pde] Re: Ant and <pde.exportPlugins> task doesn't
んー、eclipseが抱えてるantに任せるわけか。これならちょっとした環境固有のpathを与えるだけでpde.exportPluginsタスクが動かせるわけだ。
■build.xml(部分)
■バッチファイル例
eclipseを使うので、環境にeclipse(PDE)とワークスペースを事前に用意する必要があるけど、ファイルコピーするだけでいいのは助かる。
環境:
Windows XP Pro SP3
Eclipse 3.4
JDK5
プラグインのエクスポート部分をどうやってantで実行したらいいのか、という話。
pde.exportPluginsタスクというのがあるので、これをbuild.xmlに書けばいいのだけども、このタスクが実行できるほどのCLASSPATHを通すことを考えるとめまいがしてしまう。
以下のページで、eclipseが抱えているant(org.eclipse.ant.core.antRunner)から実行する例が。
[news.eclipse.platform.pde] Re: Ant and <pde.exportPlugins> task doesn't
んー、eclipseが抱えてるantに任せるわけか。これならちょっとした環境固有のpathを与えるだけでpde.exportPluginsタスクが動かせるわけだ。
■build.xml(部分)
<property name="dist" location="dist"/>
<target name="export_plugin">
<pde.exportPlugins
destination="${dist}"
exportSource="false"
exportType="directory"
plugins="com.example.plugins.id"
useJARFormat="true"/>
</target>
■バッチファイル例
SET JAVA_HOME=C:\Program Files\Java\jdk1.5.0_21org.eclipse.equinox.launcher******.jarのpathおよびファイル名は環境それぞれに合わせて。
SET PATH=%JAVA_HOME%\bin;%PATH%
java -jar C:\path\to\eclipse\plugins\org.eclipse.equinox.launcher_1.0.101.R34x_v20081125.jar -application org.eclipse.ant.core.antRunner -data "C:\path\to\workspace" -buildfile build.xml
eclipseを使うので、環境にeclipse(PDE)とワークスペースを事前に用意する必要があるけど、ファイルコピーするだけでいいのは助かる。
環境:
Windows XP Pro SP3
Eclipse 3.4
JDK5
- WSDLのバリデータなどが検証の過程でインターネットにアクセスするのでプロクシ設定が必要な環境においては、ウィンドウ→設定→インターネット→プロクシ設定で設定しておかなければならない。
- これを忘れると赤い×がつく。つい、必要なjarが参照できていないのでは?という方向に向かいがち。
- クライアントを作る分には手間も少なく、インタフェース仕様がサーバサイドで実際に使われているコードと対応するので齟齬が減らせるのではないかと思った。
- java/Eclipse/Tomcat/axisの組み合わせに関して言えば、本来やりたかったこと以外の部分で悩むことが多いんじゃないだろうか。java文化の特徴かも。
- ASP.NETの場合、メソッドの属性にメタデータが書けるせいか手間が少ない。今後アノテーションに対応してくればツールを使う部分でハマるケースを回避できるかもしれない。
- DLL HellよりもJAR Hellのがハマる
- MTでコードについて書くのは面倒。正直Wikiにしておけばよかった。
ここまででローカルPC内でウェブサービスクライアント~サービス間の疎通を行うことが出来た。
今度はサービスプロバイダを実際にサービスを公開するサーバに載せる。
今回使用したサーバは、OS=FedoraCore3、Java=JDK5.0 update5を使用している。
今度はサービスプロバイダを実際にサービスを公開するサーバに載せる。
今回使用したサーバは、OS=FedoraCore3、Java=JDK5.0 update5を使用している。
導入
- Windows上で環境構築した際に使用したzipアーカイブをlinux上でどしどし展開する。
- /home/
- apache-tomcat-5.5.15/
- apache-tomcat/ (シンボリックリンク)
- sen-1.2.1/
- sen/ (シンボリックリンク)
- /home/
- senについては、プロジェクトのホームページからダウンロードしたアーカイブでなくて、Windows上で「辞書の準備」までした状態当該ディレクトリをアーカイブし、そのままサーバ上に展開すると、辞書を改めて用意しなくていいので手間が省ける。
Tomcat
- ウェブアプリからsenを実行できるようにするため、conf/catalina.propertiesに以下の内容を追記する。
sen.home=/home/sen
- bin/startup.shを実行して起動。
デプロイ
- こんな感じのターゲットで、ローカルの資産をWARに固めてTomcatにデプロイする。そのまま、Axisでのデプロイも行って、サービスを登録する。
リモートからサービスをデプロイするには(AdminServiceを使うには)、server-config.wsddでenableRemoteAdminをtrueにしておく必要がある。org.apache.axis.utils.Adminを用いてserverサイドのconfigを「ローカルで」生成し、これを雛形にしてリモートAdminを有効にした状態で、WebContent/WEB-INF/sever-config.wsddとして放り込んだ。で、そのままTomcatにデプロイしてAdminClientからサービスをデプロイ、とゆー、大雑把な手段。。。<taskdef resource="axis-tasks.properties" /> <taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"/> <taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask"/> <property name="dist" value="./dist" /> <property name="web" value="./WebContent" /> <property name="war" value="KeitaisoWS.war" /> <target name="deploy_ws" depends="deploy2server"> <axis-admin port="8080" hostname="passing.breeze.cc" failonerror="true" servletpath="/KeitaisoWS/services/AdminService" xmlfile="./src/cc/breeze/ws/keitaiso/deploy.wsdd" username="AdminServiceを使うためのユーザ。user.lstに書いておく" password="パスワード" /> </target> <target name="make_war"> <delete dir="${dist}" /> <mkdir dir="${dist}"/> <mkdir dir="${dist}/WEB-INF"/> <mkdir dir="${dist}/WEB-INF/classes"/> <mkdir dir="${dist}/WEB-INF/lib"/> <copy todir="${dist}"> <fileset dir="${web}"> <include name="**/*.*"/> <exclude name="**/*.java"/> <exclude name="**/jsp_servlet/*.class"/> <exclude name="**/build.xml"/> <exclude name="**/build.properties"/> </fileset> </copy> <copy todir="${dist}/WEB-INF/classes"> <fileset dir="build/classes"> <include name="**/*.*"/> <exclude name="**/jsp_servlet/*.class"/> <exclude name="**/*.java"/> <exclude name="**/*.wsdd"/> </fileset> </copy> </target> <target name="deploy2server" depends="make_war,undeploy" > <delete file="${war}" failonerror="false" /> <jar jarfile="${war}" basedir="${dist}" manifest="${dist}/META-INF/MANIFEST.MF"/> <deploy url="http://passing.breeze.cc:8080/manager" username="Tomcatのmanagerを使うためのユーザ" password="パスワード" path="/KeitaisoWS" war="${war}" /> </target> <target name="undeploy"> <undeploy url="http://passing.breeze.cc:8080/manager" username="Tomcatのmanagerを使うためのユーザ" password="パスワード" path="/KeitaisoWS" failonerror="false" /> </target> - Tomcatのmanagerを使うためのユーザは、サーバ側Tomcat導入ディレクトリのconf/tomcat-users.xmlに書いておく。
- ブラウザからhttp://passing.breeze.cc:8080/managaer/htmlにアクセスしてTomcatに配備されたか確認する。
- ブラウザからhttp://passing.breeze.cc:8080/KeitaisoWS/ →「リスト」にアクセスしてサービスが配備されたか確認する。
ここまでで、ローカル動作のTomcat上で動くウェブサービスを配備できているはず。
コアロジックの確認はしたものの、あくまで単一のJavaコード内に閉じていた。SOAP経由での疎通確認用ウェブサービスクライアントを作成する。
コアロジックの確認はしたものの、あくまで単一のJavaコード内に閉じていた。SOAP経由での疎通確認用ウェブサービスクライアントを作成する。
プロジェクト準備
- プロジェクトを作成する。
- [ファイル]-[新規]-[プロジェクト]から、「/Java/Javaプロジェクト」を選択
- この時点で作るクライアントはまだサービス利用者の立場で作成するわけではなくサービスの疎通確認用の意味合い。ゆえにインタフェース変更のセンもまだまだ捨てきれず、「/Webサービス/Webサービス・クライアント」ウィザードで作ってしまうと再度クライアントコードを生成するのが手間なのでplainなJavaプロジェクトを用いる。
- プロジェクト名を適当につけてプロジェクト作成。
- [ファイル]-[新規]-[プロジェクト]から、「/Java/Javaプロジェクト」を選択
- Axisのライブラリを参照できるようにする
- プロジェクトのプロパティ→「/Javaのビルド・パス」→「ライブラリ」タブへ移動
- 「ライブラリーの追加」ボタン→「ユーザー・ライブラリ」を選択
- Axis導入時に設定した「AXIS_LIB」を選択
クライアントコード生成
- WSDLからJavaコードを生成する。TomcatにデプロイされたWSDLを参照するのではなくローカルに生成したWSDLを直接参照する。前項同様、これから先WSDLを作り直すセンがまだあり得るので、デプロイしないでローカルでどんどん試せる方がよいと思う。
<taskdef resource="axis-tasks.properties" /> <target name="make_client_hinagata"> <wsdl2java serverside = "no" output = "./src" url = "../KeitaisoWS/WebContent/wsdl/KeitaisoKaiseki.wsdl" deployscope="application" /> </target> - 生成した後は例のごとく「パッケージエクスプローラ」上で「更新」を忘れないようにする。既存のファイルの内容が更新されるだけであれば問題ないが、ファイルが増えたり減ったりした場合は明示的にプロジェクトに追加された状態を作らなければならない。これってAntタスクで書けないのかなー。
実装
- サービスを呼び出すためのコードはWSDLから生成されているので、これを利用するコードを書く
- 単純な例。
public static void main(String[] args) throws Exception { KeitaisoKaiseki binding; try { binding = new KeitaisoKaisekiServiceLocator().getKeitaisoKaisekiService(); } catch (javax.xml.rpc.ServiceException e) { throw new Exception( "" , e ); } try { KeitaisoToken[] result = null; result = binding.analyze("やっとここまで来ましたね。"); for (int i = 0; i < result.length; i++) { KeitaisoToken token = result[i]; System.out.println(token.getToken() + "," + token.getBasicString() + "," + token.getHinshi() + "," + token.getReading() + "," + token.getPronunciation()); } } catch (java.rmi.RemoteException e) { throw new Exception( "" , e ); } } - サービスのバインディングを得る際、URLオブジェクトを渡すことで任意の場所にあるサービスにバインドできる。デフォルトではWSDLに書かれたURL。WSDLには最終的にサービスを公開するURLを書いてあるので、疎通用にバインドする。
URL url = new URL("http://localhost:8080/KeitaisoWS/services/KeitaisoKaisekiService");
binding = new KeitaisoKaisekiServiceLocator().getKeitaisoKaisekiService( url );
不明点が多いだけに、これから先もTry&Errorを繰り返すことになるはず。
繰り返されることを前提とした手順を検討する。
繰り返されることを前提とした手順を検討する。
- サービスのInterfaceとやり取りするデータの入れ物を作成する。このとき、最終的な出来上がりとはパッケージを分けておく。
- インタフェースからWSDLを生成する。
- 生成したWSDLからサーバサイドのコードを生成する。
- 生成されたコード中の実装すべき部分を実装する。
- デプロイしないで、ローカルで完結し動作するまで繰り返す。
- サーバにデプロイして動作確認する。
- cc.breeze.ws.keitaiso.prototype:Interfaceとデータの入れ物
- KeitaisoToken.java([1]手書き)
- KeitaisoKaiseki.java([1]手書き)
- cc.breeze.ws.keitaiso:最終的な出来上がり
- KeitaisoKaisekiServiceLocator.java([3]生成)
- KeitaisoKaisekiServiceSoapBindingImpl.java([3]生成→[4]手書き)
- KeitaisoKaisekiServiceSoapBindingSkeleton.java([3]生成)
- KeitaisoKaisekiServiceSoapBindingStub.java([3]生成)
- KeitaisoToken.java([3]生成)
- KeitaisoKaiseki.java([3]生成)
- KeitaisoKaisekiService.java([3]生成)
インタフェース
- 次のような単純なインタフェースとした。最終的には生成した結果しか使わないので、あまりこだわらないでおく。
package cc.breeze.ws.keitaiso.prototype public interface KeitaisoKaiseki { public KeitaisoToken[] analyze( String text ) ; } - 入れ物は以下の通り。senのnet.java.sen.Tokenとほぼ同じ。WSDL生成→サーバサイドコード生成の過程でgetter/setter/コンストラクタは自動生成され、コメント含めてその他の内容は引き継げないので、フィールド(わざわざpublicにしているのはprivateだと生成対象にならないから)とコンストラクタだけ定義する。
package cc.breeze.ws.keitaiso.prototype public class KeitaisoToken { /// 分割されたトークン public String token; /// 品詞 public String hinshi; /// 基本語(活用前、みたいなイメージか) public String basicString; /// 元の文字列中における本トークンの開始位置 public int beginPosition; /// 元の文字列中における本トークンの終了位置 public int endPosition; /// トークンの長さ public int tokenLength; /// トークンの読み public String reading; /// トークンの発音 public String pronunciation; /// 付加情報 public String additionalInfo; /// 活用?? public String conjugationalForm; /// コスト public int cost; /// ?? public String surface; KeitaisoToken() { } }
WSDL生成
- 次のようなAntターゲットを使用してWSDLを生成する。
最初の一行目(taskdef)は、Antタスクを使用するための定義(build.xmlの先頭で宣言しておく)。axis-ant.jar内にあるaxis-tasks.propertiesで定義されている(わかりにくい)<taskdef resource="axis-tasks.properties" /> <target name="java2wsdl"> <mkdir dir="WebContent/wsdl"/> <axis-java2wsdl namespace = "http://breeze.cc/KeitaisoWS" classname = "cc.breeze.ws.keitaiso.prototype.KeitaisoKaiseki" output = "./WebContent/wsdl/KeitaisoKaiseki.wsdl" location = "http://passing.breeze.cc/KeitaisoWS/services/KeitaisoKaisekiService" > <classpath> <pathelement path="build/classes"/> </classpath> </java2wsdl> </target>
サーバサイドソース生成
- 次のようなAntターゲットを使用してWSDLからjavaソースを生成する。
<target name="make_server_hinagata" depends="java2wsdl"> <axis-wsdl2java serverside = "yes" output = "./src" url = "./WebContent/wsdl/KeitaisoKaiseki.wsdl" skeletonDeploy="yes" deployscope="application" /> </target>
実装
- コアロジックを記述する。「最初に作ったインタフェース名+ServiceSoapBindingImpl.java」が実装すべきソース。
- ローカル確認用のMainメソッドも作っておく。実際に配備して確認する前に、ローカルなjavaアプリとして動作を確認する。
- senに辞書その他の位置を通知しなければならないのでローカル実行時のVMオプションに「-Dsen.home=sen導入ディレクトリ」を指定すること。
- ソース生成は何回行ってもよく、****ServiceSoapBindingImpl.javaは(上記Antターゲットの場合)既に存在する場合は上書きされない。
サービスデプロイ(ローカルTomcat)
- サーバサイドのソース生成を行った際、サービスを配備するための「deploy.wsdd」が同時に生成されている。
- これを用いてAxisにサービスを登録する。以下のようなAntターゲットを実行する。
<target name="deploy_ws_local"> <axis-admin port="8080" hostname="localhost" failonerror="true" servletpath="/KeitaisoWS/services/AdminService" debug="true" xmlfile="./src/cc/breeze/ws/keitaiso/deploy.wsdd" /> </target> - サービスのデプロイが正しく完了すれば、http://localhost:8080/コンテキスト名/→「リスト」で登録したサービス名と公開オペレーションが表示される。
ここの手順では、動的Webプロジェクトを作成して、ここからウェブサービスを作っているが、本来はウェブサービスプロジェクトを作成することで同じことができるはず。。。「はず」というのは、WTPからウェブサービス生成しようとしたのだが例外が出てしまって先に進まなかったので、別のやり方を探したから。
サーブレットが動くところまで
- Eclipseを起動し、[ファイル]-[新規]-[その他]を選ぶ。
- ウィザードの選択から「/Web/動的Webプロジェクト」を選択
- プロジェクト名を適宜つける
- Target Runtimeに「Apache Tomcat v5.5」を選択。(設定しおいたサーバ一覧から選べる)
- Contextに名前を適宜つける。デフォルトはプロジェクト名と同じ。
- サーバービューにTomcatインスタンスの状態が表示されているはず。ここに今回作成したプロジェクトを割り付ける。
- Tomcatインスタンスを選択して、コンテキストメニューから「プロジェクトの追加および除去」を選ぶ。
- 「使用可能プロジェクト」に、前項で作成した「動的Webプロジェクト」があるはずなので、これを選択し、「追加」ボタンを押す。
- 選択したプロジェクトが「構成プロジェクト」に追加される。
- 「終了」を押して確定する。
- プロジェクトディレクトリ/WebContent/にAxisの必要ファイルをコピーする。lib配下だけコピーしてもいいのだけど、happyaxis.jspによる環境チェックもろもろも利用したいのでサンプルからコピーしている。
- Axis導入ディレクトリ/webapp/axis/の配下をコピーする。
- WebContent配下の次のファイルを「消す」。元々がサンプルとして提供されているものなので自前のウェブサービスでは使用しない。
- WebContent/WEB-INF/classes/samples/配下全部
- WebContent/WEB-INF/StockQuoteService.jws
- ファイルシステム上でプロジェクトディレクトリ配下に何か置いてもEclipseのプロジェクトの一部として認識されない。プロジェクトエクスプローラ上で「更新」を行うことを忘れないようにする。WTP管理下のTomcatに対しては、プロジェクトの一部として認識されているファイルだけがデプロイされるから。
- Tomcatを起動し、必要な環境が準備できたか確認する。
- サーバービューでサーバを選択し、「始動」を選択する
- サーバが起動したら、ブラウザで「http://localhost:8080/コンテキスト名/」にアクセスする。AxisのWelcomeページが表示されるはず。
- Welcomeページから「検証」をクリックして、必要なコンポーネントが揃っているか(ロードされているか)確認する。
- WTPから起動したTomcatが認識しているモノ(デプロイされた資産など)を見たい場合は、「Eclipseのワークスペース/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/配下を参照する。意図通りのファイルが配備されているか確認できる。(tmp0は変化する、のだろう)
コンテキスト固有の設定
- axisの配布物の中からweb.xmlを持ってきたのでdisplay-nameがデフォルトのままである。せっかくなので自分のwebアプリであることがわかるように編集する。
- WebContent/WEB-INF/web.xmlを編集
- /web-app/display-nameに表示名を入れる。
- 今回作成するウェブサービスはsenを用いて形態素解析を行う。したがって、senのライブラリがロードされるようにしなければならない。これはコンテキスト固有の問題と思われるのでWebContent配下に配置した。
- sen導入ディレクトリ/lib/sen.jar → WebContent/WEB-INF/lib/へコピー
-
senを使用するにはシステムプロパティsen.homeにsen導入ディレクトリを指定しなければならない。Tomcat導入ディレクトリ/conf/catalina.propertiesに記述することができるのだが、WTP管理下のTomcatの場合はここに書いても反映されない。
パッケージエクスプローラーで見たとき「サーバー」というノードに上記で定義したWTP管理下のTomcatインスタンスの設定が見えるはず。ここにcatalina.propertiesを追加する。そして中身をTomcat導入ディレクトリ配下のcatalina.propertiesからコピーしてくる。その上で「sen.home=sen導入ディレクトリ」を追記する。
sen.homeだけを記述するとTomcatが起動しなくなるので注意。 - 上記のようにデプロイされる内容に変化があった場合、WTP管理下のTomcatであればその場で再デプロイされるので便利。ただし、「プロジェクトの一部」として認識されていないといけないので、ファイルやディレクトリをコピーした後は「更新」を忘れないこと。
