XDocletの基本はDoclet、そしてそのルートはJavaDocになります。ではDocletとはどのような物なのでしょうか?
以下はSunのDoclet概要のページにあったサンプルをDOCLET APIを参考に多少変更してみたものです。
import com.sun.javadoc.*; // このパッケージが必須です。
public class ListClass {
public ListClass(){}
public ListClass(String str){}
private String str_field;
public static boolean start(RootDoc root) {
// エントリーポイントです。
// メソッド名やパラメタはmainメソッドと同じくDocletでは固定です。
ClassDoc classes = root.classNamed("ListClass");
ConstructorDoc[] constor = classes.constructors();
for (int i = 0; i < constor.length; ++i) {
System.out.println(constor[i]);
}
MethodDoc[] methoder = classes.methods();
for (int i = 0; i < methoder.length; ++i) {
System.out.println(methoder[i]);
}
return true;
}
}
|
コンパイルと実行は以下のようにします。Doclet APIはtools.jarに含まれているので、Java\binへのパスだけでは不十分です。以下では、Doclet制御クラス自身を解析対象にしています。
D:\doclet>javac -classpath c:\jdk1.3.1_02\lib\tools.jar ListClass.java D:\doclet>javadoc -doclet ListClass -classpath c:\jdk1.3.1_02\lib\tools.jar ListClass.java ソースファイル ListClass.java を読み込んでいます... Javadoc 情報を構築しています... ListClass // コンストラクタは2種類 ListClass start // メソッドは1種類だけ |
XDocletも当然ですがオープンソースで、HPはhttp://xdoclet.sourceforge.net/になります。そのHPを参考に、XDocletの概要を整理したいと思います。XDocletのポイントを挙げると以下のようになるでしょうか。
XDocletはAntと組み合わせてコマンドベースでの使用はもちろん普通の方法ですが、最近はEclipseとそのPlugInを使うことで意識せずに使われるようになりました。もちろんこのようなAttribute Oriented Programmingと呼ばれるものだけで必要なコードを全て作成することは出来ませんし、ましてLombozのようなIDEからXDocletのコードを自動作成する場合には、最終のコード生成率は更に低下します。したがって、XDocletは”さら”でコードを書ける前提でユーザからトリビアルなタスクを軽減するのが目的と考えるのが適切でしょう。
ここではLombozのCMP EntityBean作成時にXDocletがどのように使われているかを見てみる事にします。
[Lombozで生成されたソース、一部マニュアルで修正]
/*
* 作成日: 2003/07/06
*
* この生成されたコメントの挿入されるテンプレートを変更するため
* ウィンドウ > 設定 > Java > コード生成 > コードとコメント
*/
package com.nemuneko.cmp;
import javax.ejb.EntityBean;
/**
* @ejb.bean name="Address"
* jndi-name="AddressBean"
* type="CMP"
* primkey-field="name"
* schema="mySchema"
* cmp-version="2.x"
*
* @ejb.persistence
* table-name="address"
*
* @ejb.finder
* query="SELECT OBJECT(a) FROM mySchema as a"
* signature="java.util.Collection findAll()"
*
**/
public abstract class AddressBean implements EntityBean {
/**
* The ejbCreate method.
*
* @ejb.create-method
*/
public java.lang.String ejbCreate(String name, String phone, String address) throws javax.ejb.CreateException {
// EJB 2.0 spec says return null for CMP ejbCreate methods.
// TODO: YOU MUST INITIALIZE THE FIELDS FOR THE BEAN HERE.
// setMyField("Something");
setName(name);
setPhone(phone);
setAddress(address);
return null;
}
/**
* The container invokes this method immediately after it calls ejbCreate.
*
*/
public void ejbPostCreate(String name, String phone, String address) throws javax.ejb.CreateException {
}
/**
* Returns the phone
* @return the phone
*
* @ejb.persistent-field
* @ejb.persistence
* column-name="phone"
* sql-type="VARCHAR"
*
* @ejb.interface-method
*/
public abstract java.lang.String getPhone();
/**
* Sets the phone
*
* @param java.lang.String the new phone value
*
* @ejb.interface-method
*/
public abstract void setPhone(java.lang.String phone);
/**
* Returns the name
* @return the name
*
* @ejb.persistent-field
* @ejb.persistence
* column-name="name"
* sql-type="VARCHAR"
* @ejb.pk-field
* @ejb.interface-method
*/
public abstract java.lang.String getName();
/**
* Sets the name
*
* @param java.lang.String the new name value
*
* @ejb.interface-method
*/
public abstract void setName(java.lang.String name);
/**
* Returns the address
* @return the address
*
* @ejb.persistent-field
* @ejb.persistence
* column-name="address"
* sql-type="VARCHAR"
*
* @ejb.interface-method
*/
public abstract java.lang.String getAddress();
/**
* Sets the address
*
* @param java.lang.String the new address value
*
* @ejb.interface-method
*/
public abstract void setAddress(java.lang.String address);
}
|
では上記のソースプログラムを使用して各インターフェース、クラス、DDを作成するためのファイルはどのようになっているのでしょうか?
Antのビルドファイルbuild.xml中ではejbGenerate.xmlをサブタスクとして呼び出しています。さらにejbGenerate.xml中ではejbdocletタグがXDoclet処理のために存在しています。ejbdocletタグ中で必要とされる全てのインターフェース、クラス、DDが生成され、その後は通常のAntタスクです。
[build.xml部分]
<!-- Run EJBDoclet -->
<target name="Ejbdoclet" depends="init">
<ant antfile="META-INF/ejbGenerate.xml" dir="." target="ejbdoclet">
<property name="container" value="${container}"/>
<property name="deploy.dir" value="${deploy.dir}"/>
<property name="src.dir" value="${src.dir}" />
<property name="ejbsrc.dir" value="${ejbsrc.dir}" />
</ant>
</target>
[ejbGenerate.xml部分]
<ejbdoclet
destdir="${ejbsrc.dir}"
mergedir="${ejb.dd.dir}"
excludedtags="@version,@author,@todo"
; excludedtags属性はXDocletタグから除外ということでしょうか。
addedtags="@lomboz generated"
ejbspec="2.0"
force="${xdoclet.force}"
verbose="true" >
<fileset dir="../src" defaultexcludes="yes">
<patternset includesfile="META-INF/ejbs.xml" />
</fileset>
<dataobject/>
<valueobject/>
<utilobject cacheHomes="true" includeGUID="true"/>
; Home interfaceのルックアップ処理は高コストなので、キャッシュするのはEJBのパターンです。
AddressUtilクラスのメソッドとして作成されています。
またincludeGUIDはユニークなキー生成のための設定です。
キーとはプライマリーキーの場合が一般的かとは思いますが、限定はされません。
cacheHomesと同じく、AddressUtilクラス中でメソッドが作成されています。
ソースを見ると分かるのですが、サーバのIPアドレスと現在時刻それとHashを使って作成されます。
<remoteinterface/>
<localinterface/>
<homeinterface />
<localhomeinterface/>
<entitypk/>
<entitycmp/>
<entitybmp/>
<session/>
<deploymentdescriptor
destdir="${ejb.dd.dir}"
validatexml="false"
mergedir="${ejb.dd.dir}"
/>
<!--
Have struts form objects generated based on entity beans'
data objects. Will require struts.jar to compile. -->
<strutsform />
<!--
Have a mapping.xml file generated for castor classes.
-->
<castormapping destdir="${ejb.dd.dir}" validatexml="false" />
<!-- -->
<weblogic
version="6.1"
xmlencoding="UTF-8"
destdir="${ejb.dd.dir}"
validatexml="false"
datasource="PLEASE_MODIFY_THIS"
mergedir="${ejb.dd.dir}"
persistence="weblogic"
/>
<jboss
version="3.0"
unauthenticatedPrincipal="nobody"
xmlencoding="UTF-8"
destdir="${ejb.dd.dir}"
validatexml="false"
datasource="java:/DefaultDS"
datasourcemapping="mySQL"
preferredrelationmapping="foreign-key"
/>
<jrun
version="4.0"
xmlencoding="UTF-8"
destdir="${ejb.dd.dir}"
validatexml="false"
/>
<webSphere destdir="${ejb.dd.dir}"/>
<jonas
version="2.5"
xmlencoding="UTF-8"
destdir="${ejb.dd.dir}"
validatexml="false"
mergedir="${ejb.dd.dir}"
/>
<orion
destdir="${ejb.dd.dir}"
/>
<apachesoap
destdir="${ejb.dd.dir}"
/>
</ejbdoclet>
|