顯示具有 開發:Maven1 標籤的文章。 顯示所有文章
顯示具有 開發:Maven1 標籤的文章。 顯示所有文章

2007年8月5日 星期日

(2006.08月號--151期) 如何以Maven協助Hibernate開發

在今日的企業級應用開發環境中,物件導向開發方式已成為主流,但在實際的應用中,物件只存在於程式與記憶體之中,並不能直接進行儲存。如果想要永久保存物件狀態,則必須要進行物件的持久化,也就是把物件狀態儲存進專門的資料庫系統中,而目前最廣泛使用的關聯式資料庫(RDBMS),並不支援儲存物件導向數據資料。


因此傳統的關聯式資料庫程式設計,必須直接在程式中以Hard code的方式撰寫SQL語法進行開發,而以Hard code撰寫SQL語法的方式,相對的也代表了開發的應用程式無法跨資料庫平台。雖然JDBC統一了Java程式與資料庫之間的操作介面,讓開發人員可以不用了解與資料庫相關特定的API操作,然而自行撰寫SQL語法或再次將SQL語法進行封裝仍是不可避免的事,而在物件導向程式設計之中,物件與關聯式資料並無法以簡單的方式進行轉換,導致了資料永續性的開發上受到了先天的限制。


ORM(Object/Relational Mapping)物件-關聯映射技術,在該問題的處理上已經有完善的解決方案,而目前在開放原始碼的技術中,最受人矚目的ORM實作,應該就是Hibernate了。在本期中筆者將介紹如何使用Maven協助Hibernate進行快速開發,而對於Hibernate的基本觀念,筆者在此並不詳述,請參考之前刊載於RunPC中的相關文章。


註:關於Hibernate文章,請參考RunPC128129130132期,資料永續層解決方案Hibernate的相關介紹,作者為Mark Ho


設定hibernatedoclet環境

在開發過程中Hibernate進行物件與關聯式資料的處理過程中,需要有一份映射文件,用以描述物件與關聯式資料的轉換關係,例如:資料型態的對應、資料欄位長度的限制、單向關聯、雙向關聯、一對多、一對一與多對多關聯等等的設定。一般而言映射文件命名通常為*.hbm.xml


然而在實際開發上當物件作了修改或重構後,必須對映射文件進行維護。如此的開發方式相當的不直覺與不方便。並且學習映射文件的撰寫不是一件簡單的事。


因此我們換一個角度思考,如果在物件的設計中,順手將這些轉換關係的資訊寫在物件中,由物件編譯過程中直接幫我們產生相關的映射文件,則我們只要維護一份原始碼即可,當進行修改與重構的過程中,映射檔案的變更也將立即的反應出來。在此我們將使用XDoclet進行Code generate來達到這個目的。


註:有關於XDoclet相關觀念的文章,請參考RunPC130131期,XDoclet入門篇與進階篇,作者為歐宣修。


首先請參考筆者於148期中的Maven文章,使用Maven Genapp Plugin產生專案,並且參考設定 1project.xml進行修改,在該檔中可以看到我們在Maven相依的plugin套件中加入了maven-xdoclet-plugin。接著參考設定 2project.properties加入設定參數。說明請參考表 1中關於hibernatedoclet的參數設定。最後再對maven.xml進行設定,參考設定 3中可知當我們執行java:compile這個goal前會先行運作xdoclet:hibernatedoclet協助我們產生Mapping file


設定 1 project.xml

<?xml version="1.0" encoding="UTF-8"?>

<project>

<pomVersion>3</pomVersion>

<artifactId>hibernate-example</artifactId>

<groupId>hibernate-example</groupId>

<name>Example Hibernate Application</name>

<currentVersion>1.0</currentVersion>

<dependencies>

<dependency>

<groupId>xdoclet</groupId>

<artifactId>maven-xdoclet-plugin</artifactId>

<version>1.2.3</version>

<type>plugin</type>

</dependency>

中間省略…

</dependencies>

中間省略…

</project>


設定 2 project.properties

#####################################

# hibernatedoclet properties

#####################################

maven.xdoclet.hibernatedoclet.fileset.0=true

maven.xdoclet.hibernatedoclet.fileset.0.sourcedir=${maven.src.dir}/java

maven.xdoclet.hibernatedoclet.fileset.0.include=**/*.java

maven.xdoclet.hibernatedoclet.fileset.0.exclude=

maven.xdoclet.hibernatedoclet.hibernate.0=true

maven.xdoclet.hibernatedoclet.hibernate.0.Version=3.0

maven.xdoclet.hibernatedoclet.destDir=${maven.build.dest}


設定 3 maven.xml

<?xml version="1.0" encoding="UTF-8"?>

<project

xmlns:j="jelly:core"

xmlns:ant="jelly:ant">

<preGoal name="java:compile">

<attainGoal name="xdoclet:hibernatedoclet"/>

</preGoal>

</project>


1 maven-xdoclet-plugin設定

參數

說明

maven.xdoclet.hibernatedoclet.fileset.[index]

設定hibernatedoclet引用的檔案集設定,index值為0~910組設定,也就是說最多可以針對10組路徑下的原始檔進行hibernatedocletCode generate,預設index=0為啟用。若指定的maven.xdoclet.hibernatedoclet.fileset.[index]未設定,則以下相對應的檔案集設定即無效。

maven.xdoclet.hibernatedoclet.fileset.[index].sourcedir

指定檔案集原始碼放置路徑,預設為${maven.src.dir}/java

maven.xdoclet.hibernatedoclet.fileset.[index].include

指定檔案集引入檔案規則,預設為**/*.java

maven.xdoclet.hibernatedoclet.fileset.[index].exclude

指定檔案集排除檔案規則,預設不排除任何檔。

maven.xdoclet.hibernatedoclet.hibernate.[index]

設定對maven.xdoclet.hibernatedoclet.fileset.[index]進行hibernatedoclet是否生效,預設為true,若為false則對指定的檔案集不執行hibernatedoclet

maven.xdoclet.hibernatedoclet.hibernate.[index].Version

設定對maven.xdoclet.hibernatedoclet.fileset.[index]進行hibernatedoclet所產生的*.hbm.xml檔案可使用於hibernate那個版本,預設為1.1版,目前可設定的值為目前有1.12.02.13.0

maven.xdoclet.hibernatedoclet.destDir

所有檔案集產生出的*.hbm.xml放置的目的路徑。


建立Domain Object並產生Mapping File

當上一節的設定完成後,我們正式建立Domain Object,並試圖產生Hibernate所需要的Mapping file。這裏我們

是以POJO定義Domain Object,而POJO就是所謂的Plain Ordinary Java Object,字面上來講就是無格式普通Java 物件,簡單的可以理解爲一個不包含邏輯程式碼的值物件。


我們在此建立二個POJO,分別是User(使用者)Role(所屬角色)作為開始。這時Maven專案架構應該如資料結構 1所示,圖 1展示了類別屬性與其setget相關method。接著參考程式 1、程式 2分別在類別中加入<hibernatedoclet>Tag設定,在此我們簡單的了解一下相關屬性用途,若讀者希望了解更詳細的屬性設定,可參考XDoclet官方網站http://xdoclet.sourceforge.net/xdoclet/tags/hibernate-tags.html


  1. @hibernate.class:該Tag用於宣告定義持久化類別,需放置於Class宣告的上方。

    1. table:指定持久化類別對應資料庫Table名稱。

  2. @hibernate.id:定義主鍵欄位相關屬性。

    1. column:指定欄位對應資料庫欄位名稱,若未指定則使用相對應的property名稱。

    2. generator-class:主鍵產生器屬性,目前有uuid.hexuuid.stringincrementassignednativeidentitysequencehiloseqhiloforeign可供設定。

  3. @hibernate.property:定義持久化類別對應資料庫欄位相關屬性。

    1. column:指定欄位對應資料庫欄位名稱,若未指定則使用相對應的property名稱。

    2. length:指定對應資料庫欄位長度。

    3. not-null:是否允許欄位存放null值。

    4. unique:是否允許資料庫欄位值重覆

    5. type:對應資料庫欄位型態

另外,讀者可能發現,在範例中的二個POJO都實現了java.io.Serializable介面,這是Hibernate規範中所提到的,但目前大多數資料並未說明為何必須實現Serializable。事實上Hibernate並不要求持久化類必須實現Serializable介面,但是對於採用分散式架構的Java應用,當Java物件在不同的分散節點之間傳輸時(例如:RMI),這個物件所屬的類別必須實現Serializable介面,此外,在Java Web應用中,如果希望對HttpSession中存放的Java物件進行持久化,那麼這個Java物件所屬的類也必須實現Serializable介面,因此若只拿Hibernate作單純資料庫應用可不遵守該規定外,HibernateJ2EE的應用上都是必須實現Serializable的。


經過maven的設定與撰寫完POJO後,在Console下執行maven java:compile,當指令運作結束後,在[hibernate_example/target/classes/demo/model]路徑下將會出現User.hbm.xmlRole.hbm.xmlHibernate Mapping file

資料結構 1

hibernate-example

|--src

| --main

| --java

| --demo

| --model

| |--Role.java

| --User.java

|--maven.xml

|--project.properties

--project.xml


1 Domain Object類別圖


程式 1 User.java

package demo.model;

import java.io.Serializable;


/**

* @hibernate.class table="app_user"

*/

public class User implements Serializable {

private Long id;

private String username;

private String password;


/**

* @hibernate.id column="id" generator-class="native"

*/

public Long getId() {

return id;

}


/**

* @hibernate.property length="50" not-null="true" unique="true"

*/

public String getUsername() {

return username;

}


/**

* @hibernate.property column="password" not-null="true"

*/

public String getPassword() {

return password;

}

…set method 省略…

}


程式 2 Role.java

package demo.model;

import java.io.Serializable;


/**

* @hibernate.class table="role"

*/

public class Role implements Serializable {

private Long id;

private String name;

private String description;


/**

* @hibernate.id column="id" generator-class="native"

*/

public Long getId() {

return id;

}


/**

* @hibernate.property column="name" length="20"

*/

public String getName() {

return name;

}


/**

* @hibernate.property column="description" length="64"

*/

public String getDescription() {

return description;

}

…set method 省略…

}


使用Hibernate Plugin建立Table Schema

到目前為止我們設定了hibernatedoclet、建立了POJO的類別並自動產生了相關的Mapping file。假設這時資料庫Schema早已建立好的話,就可以開始使用Hibernate進行DAO的開發與測試了。倘若Schema尚未建立呢?需要自行建立嗎?而自行建立則又會引發另一個問題,POJO的設計是基於Schema所建立的。當日後Schema修改時也必須對應相關的POJOMapping file進行維護。一次、二次還可以接受,但在專案開發的過程中需求永遠在變化,很難保證Schema不會一直變更,一旦次數一多,將引發難以重構的災難。


Ant中可以透過net.sf.hibernate.tool.hbm2ddl.SchemaExportTask協助我們將*.hbm.xml再次轉化成相對應資料庫的Schema,而在Maven上可以使用Hibernate Plugin協助進行SchemaExport。基於某些因素Hibernate Plugin最新版本為1.3並且已有一段時間沒有新版本釋出,該版本並未支援Hibernate 3.X,若讀者使用Hibernate 2開發的話,可以直接進行下載。在本次範例中我們將下載經過1.3版本修改的Hibernate Plugin1.4 (請讀者參考相關資源4進行下載)


[%MAVEN_HOME%/plugins]下的1.3版移除,放置下載完成的maven-hibernate-plugin-1.4.jar於該資料夾內即完成了hibernate plugin的安裝。接著參考設定 4project.xml中加入相依套件,此處的設定中我們加入了maven-hibernate-plugin的設定與Hibernate進行SchemaExport時所需的套件,另外為了示範Hibernate跨資料庫平台的特色,在此引入而PostgreSqlHsqldb二個開放原始碼資料庫的JDBC協助進行測試。


在進行SchemaExport功能時可以設定是否正式進行資料庫的更新,或者只是單純的產生資料庫Schema的文字檔供開發人員進行修改或使用。為此必須正確設定Hibernate與資料庫連結的相關參數,參考設定 5與設定 6比較範例中二種資料庫設定的異同處,而關於hibernate.dialect參數的設定,請參考Hibernate官方參考手冊。


最後參考設定 7加入Hibernate plugin所需的參數,讀者對照表 2即可知相關參數的意義為何。當設定完成後於Console下執行maven hibernate:schema-export即可進行SchemaExport,在此我們分別為了PostgreSqlHsqldb作了SchemaExport,請參考圖 2與圖 3的結果。若讀者不希望直接進行資料庫的更新,則可將project.properties檔中參數maven.hibernate.text設為yes再次進行hibernate:schema-export,即可於[target/schema/schema.sql]檔中查看匯出的資料庫SQL語法。


hibernate:schema-export這個goal可以協助我們進行建立資料庫Schema,而當進行POJO的修改並重新產生Mapping file時,可運行maven hibernate:schema-update,該指令將可在不移除資料表的清況下進行資料庫Schema的更新,對於DataBase Schema的重構有很大的幫助。


設定 4 project.xml增添相依套件

<dependency>

<groupId>maven</groupId>

<artifactId>maven-hibernate-plugin</artifactId>

<version>1.4</version>

<type>plugin</type>

</dependency>

<dependency>

<groupId>postgresql</groupId>

<artifactId>postgresql</artifactId>

<version>8.1-407.jdbc3</version>

</dependency>

<dependency>

<groupId>hsqldb</groupId>

<artifactId>hsqldb</artifactId>

<version>1.7.3.3</version>

</dependency>

<dependency>

<groupId>geronimo-spec</groupId>

<artifactId>geronimo-spec-jta</artifactId>

<version>1.0.1B-rc4</version>

</dependency>

<dependency>

<groupId>commons-lang</groupId>

<artifactId>commons-lang</artifactId>

<version>2.1</version>

</dependency>

<dependency>

<groupId>hibernate</groupId>

<artifactId>hibernate</artifactId>

<version>3.0.5</version>

</dependency>


設定 5 database-postgresql.properties

hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

hibernate.connection.url=jdbc:postgresql://localhost/demo

hibernate.connection.driver_class=org.postgresql.Driver

hibernate.connection.show_sql=true

hibernate.connection.username=test

hibernate.connection.password=test


設定 6 database-hsqldb.properties

hibernate.dialect=org.hibernate.dialect.HSQLDialect

hibernate.connection.url=jdbc:hsqldb:hsql://localhost/demo

hibernate.connection.driver_class=org.hsqldb.jdbcDriver

hibernate.connection.show_sql=true

hibernate.connection.username=sa

hibernate.connection.password=


設定 7 project.properties增添相關屬性設定

#####################################

# hibernate properties

#####################################

maven.hibernate.properties=${basedir}/database-postgresql.properties

#maven.hibernate.properties=${basedir}/database-hsqldb.properties

maven.hibernate.quiet=no

maven.hibernate.text=no

maven.hibernate.drop=no

maven.hibernate.delimiter=;

maven.hibernate.output.dir=${maven.build.dir}/schema

maven.hibernate.output.file=${maven.hibernate.output.dir}/schema.sql


2 maven-hibernate-plugin設定

參數

說明

maven.hibernate.properties

hibernate參數設定檔放置路徑,無論直接進行資料庫的更新或單純產出Schema的文字檔,皆必須要進行該項設定主要以hibernate.dialect參數決定使用何種資料庫方言。

maven.hibernate.text

若為yes則執行Schema Export時將直接匯出SQL成文字檔,並不會進行資料庫的更新,若設為no則直接進行資料庫Schema的更新,預設為no

maven.hibernate.quiet

是否顯示詳細的Schema Export資訊,例:當maven.hibernate.text=nomaven.hibernate.quiet=yes時,將直接進行資料庫Schema的更新,Console下不會顯示任何訊息,預設為yes

maven.hibernate.drop

是否只執行drop資料表的動作,若maven.hibernate.text=yes則匯出SQL檔將只包含drop tableSQL語法。

maven.hibernate.delimiter

分隔SQL Commands的定義符號,預設值為空字串,而大多數狀況使用”;”作為分隔符號。

maven.hibernate.output.dir

maven.hibernate.text=yes,匯出檔放置資料夾,預設為${maven.build.dir}/schema

maven.hibernate.output.file

maven.hibernate.text=yes,匯出檔放置路徑,預設為${maven.hibernate.output.dir}/${maven.final.name}-schema.sql


2 PostgreSql SchemaExport結果

3 Hsqldb SchemaExport結果


結論

Hibernate是一個開放原始碼的物件關聯映射技術,針對了JDBC進行了輕量化的將關聯資料轉化為物件封裝,使得開發人員可以隨心所欲的使用物件導向思維來駕馭資料庫。而在本期的介紹中我們了解了使用Maven協助Hibernate建立Domain Object的一種應用,而除了本次範例的方式外,Hibernate也可以使用Middlegen的方式透過現有的資料庫Schema產生出Mapping file,又或著從Mapping file建立出SchemaJava Domain Object。因此可依專案的狀況進行彈性的調整。


Hibernate的應用可以切入在任何使用JDBC的場合,既可以應用於單純的Java Application,也可以在Servlet/JSPWeb應用程式中使用,更可以應用在EJBJ2EE架構中取代CMP,完成資料持久化的重任。倘若更深入Hibernate的架構與觀念中,則將更了解物件關聯映射技術的最佳化實踐。

Read More......

2007年7月29日 星期日

(2006.05月號--148期)開放原始碼專案管理工具_建立專案與IDE環境設定

在以往初步建立專案的過程中,多數傾向於二種方式,其一是依賴IDE,建立專案同時啟動專案精靈,顯示多個專案範本,藉由開發人員選擇範本的不同進行專案的初始化並建立符合需求的開發環境設定,並在IDE上進行建置、佈署、測試。這種方式的優點是方便、易學、無需了解複雜的環境配置即可進行。而缺點是受限於IDE的開發方式,只適用公司內部或單一公司實作的小型專案,也必須限制使用特定的IDE進行開發。對於大型或是多數委外進行開發的專案則不適用,因為我們無法強制委外的開發廠商使用統一的IDE進行開發。


其二是將專案建置邏輯撰寫於Ant 腳本中,由Ant協助進行程式碼的編譯、專案的佈署、單元測試、整合測試等的建置步驟。其優點是開發過程中完全不依賴IDE開發環境,因此在目前在大型的J2EE專案中幾乎都看的到Ant的蹤跡。而缺點是Ant的腳本並沒有一定的建置邏輯與規範,因此在目前業界中Ant腳本在各專案中可重覆使用的機率並不高,並且必須因應專案特性的不同而必須重寫或修改Ant的建置腳本,但在進行中大型專案裡維護Ant並不是件容易的事,而另一個缺點是Ant無法協助我們進行IDE環境的設定,因為充其量Ant只包含建置邏輯罷了。


在本期的內容中,筆者將為各位介紹Maven是如何協助我們從無到有建立出所需的專案開發環境,並且配合多個不同的IDE Plug-in協助我們建立EclipseJBuilderIntelliJ IDEAIDE開發環境的配置。

Maven Genapp Plugin

一般習慣使用IDE的開發人員,通常都是使用專案精靈工具,套用範本來建置專案。假若Maven專案的建置必須像Ant腳本一樣無中生有的自行撰寫的話,大概就不會有太多人嚐試用Maven進行開發了。而Maven Genapp Plugin如同專案精靈一樣,透過對話的方式,輔助我們建立新專案。


在開始實作之前,請先確認Maven環境中的Genapp Plugin的版本為何,但如何得知目前版本呢,可至[%MAVEN_HOME%/plugins]資料夾下查看是否有maven-genapp-plugin-x.x.jar或者在[${maven.home.local }/cache]下是否包含著maven-genapp-plugin-x.x的資料夾。我們可以從這二個地方即可得知目前版本訊息。至筆者截稿為止,版本為2.3,若讀者手上的版本稍舊的話,可透過執行maven plugin:download -DgroupId=maven -DartifactId=maven-genapp-plugin -Dversion=2.3進行下載更新。


為什麼要先確認Genapp的版本呢,其原因在於Maven在發展過程中有其建議的資料夾結構,參考資料結構 1,而在2.3之前版本中的專案範本,並沒有完全依照如此的資料夾結構進行設置。因此在建立出的專案中會與資料結構 1的格式稍有不同,但若不下載新版的maven-genapp-plugin也不影響往後專案的建置,因為Maven只建議開發人員,統一的資料夾結構對開發是有幫助的,若專案開發環境有自己的資料夾結構或是原先由Ant建置的專案欲移轉到Maven上進行開發,都可以依照專案原本的資料夾結構進行建置。


接下來參考圖 1執行maven genapp並依提示輸入,使用專案範本、專案根路徑名稱、專案ID、專案名稱、原始碼的Package名稱,當輸入完成後立即建置出所需的專案架構供開發人員進行下一步的開發。這樣的功能就如同IDE工具的專案精靈一樣方便,但有個問題,GenApp Plug-in究竟提供多少範本協助開發人員建置專案呢?這個部分可參考相關資源1GenApp官方網站,另外也可以直接查看[${maven.home.local }/cache/maven-genapp-plugin-2.2/plugin-resources]資料夾清單,相關內容說明可參考表 1


到目前為止我們已經可以很輕鬆的建立起一個新專案,而且完全不相依於任何IDE。假若Maven能順便協助開發人員將IDE開發環境設定好那可就更完美了!那是當然的,接著筆者將介紹Maven IDE Plug-in的使用。


1 範本清單

範本名稱

範本說明

default

簡易的應用程式專案。

ejb

簡易的EJB專案。

struts

Web應用程式專案並且以StrustMVC的架構。

struts-jstl

Web應用程式專案並且使用StrustJSTL架構。

struts-validation

Web應用程式專案並且使用StrustValidation架構。

tapestry

Web應用程式專案並且以TapestryMVC的架構。

web

簡易的Web應用程式專案。

web-jstl

簡易的Web應用程式專案並且使用JSTL

web-velocity

Web應用程式專案並且以VelocityMVC的架構。

complex

建立一個複雜的J2EE專案,產生multiproject架構。


資料結構 1 Maven建議資料夾結構

/

+- src/ --專案相關代碼路徑

| +- main/ --主要程式碼路徑

| | +- java/ --java原始碼路徑

| | | +- ...

| | +- resources/ --所需資源與設定路徑

| | +- ...

| +- test/ --測試程式碼路徑

| | +- java/ --java測試原始碼路徑

| | | +- ...

| | +- resources/ --測試用資源與設定路徑

| | +- ...

| +- site/ --建立專案站台設定路徑

| +- xdoc/

| +- ...

+- target/ --專案建置成品放置

| +- ... 與其他建置中暫存路徑

+- project.xml --POM設定檔

+- README.txt

+- LICENSE.txt


1 Maven Genapp

Maven Eclipse Plugin

透過圖 1的操作我們已經建立好套用web範本的MyWebApp專案,接著進入建立好的MyWebApp資料夾中並執行maven eclipse,這時Maven將協助開發人員建立Eclipse的開發環境設定。

當產生出環境設定之後,啟動Eclipse並於功能表中執行[File][Import…],進入匯入專案作業。在對話視窗中選擇[Existing Projects into Workspace]進行下一步,接著在對話框中[Select root directory:]選項執行[Browse],並指向產生專案的路徑[D:DevMyWebApp],最後按下[Finish]即可。


第一次在Eclipse中匯入Maven建立的專案時,Eclipse[Problems]標籤將會判斷出多項錯誤,分別是[The project cannot be built until build path errors are resolved]與多個[Unbound classpath variable: 'MAVEN_REPO/xxxx/jars/xxxx-xx.xx.jar' in project MyWebApp MyWebApp]。這些錯誤的原因在於Maven使用了MAVEN_REPO參數設定相關JAR檔的關聯,而Eclipse開發環境中尚未設定該參數所致。


解決方式有二種,其一執行[maven eclipse:add-maven-repo -Dmaven.eclipse.workspace=%WORKSPACE_PATH%]可透過Maven Eclipse Plugin進行MAVEN_REPO的參數設定至[%WORKSPACE_PATH%]指定的workspace路徑下。執行完成後,必須重新啟動Eclipse才會生效。


其二可參考圖 2直接在Eclipse功能表中點選[Window][Preferences],在對話視窗中選擇[Java][Build Path][Classpath Variables]進行Classpath參數的設定,新增MAVEN_REPO參數,並將路徑指向[${maven.home.local }/repository]下即可。Maven預設repository的路徑指向[C:Documents and Settings%登入帳號%.mavenrepository]


當完成設定後Eclipse將依目前設定重新建置專案,結果如圖 3所示,這時之前的錯誤訊息將不會再出現。因此日後在專案開發的過程中當我們修改過project.xmlPOM設定或者新增、移除JAR的的相依關聯性時,只需再次執行maven eclipse就可依project.xml的設定重新產生相符的Eclipse開發環境配置。如此的Plugin不管在那個專案中都非常方便。


2 設定Eclipse MAVEN_REPO參數


3 開啟專案的Eclipse IDE

Maven JBuilder Plugin

目前業界進行J2EE專案開發最常用的IDE,除了Eclipse之外我想另一個廣受歡迎的就是JBuilder吧。在此也不避諱的為各位介紹一下如何使用Maven JBuilder Plugin協助我們建立環境設定。


在此我們直接執行maven jbuilder,若無意外的話將會看到BUILD SUCCESSFUL的訊息並且產生本範例中的[D:DevMyWebAppMyWebApp.jpx]的環境設定檔,但若發生BUILD FAILED的狀況的話,筆者大概可以猜測出讀者應該使用的是20052006JBuilder版本。


其原因在於截至目前為止,Maven JBuilder Plugin的版本為1.5,在目前的版本中預設並未支援JBuiler2005之後的版本設定。但若是因為如此而造成JBuilder的愛用者對Maven興趣缺缺的話,那可就是筆者的罪過了。


雖然預設的Plugin尚未支援新版的JBuilder,但只需進行小部分修正即可排除這個問題。首先至[${maven.home.local }/cache/maven-jbuilder-plugin-1.5]路徑下開啟plugin.jelly檔案,在檔案開頭附近很容易可以找到有一行的設定如[value=".jbuilderX,.jbuilder9,.jbuilder8,...]將其修改為[value=".jbuilder2005,.jbuilderX,.jbuilder9,.jbuilder8,...]參考設定 1,存檔後再次執行maven jbuilder即可。


這時我們將會發現Maven JBuilder Plugin正確無誤執行完成,也就是說事實上JBuilder每一個不同版本在安裝時會將相關設定檔存放至UserHome下,例如JBuilder2005存放路徑為[C:Documents and Settings%登入帳號%.jbuilder2005],因此我們只需將設定的版本名稱加入plugin.jelly中就可使Maven JBuilder Plugin支援該版本。


接著只要在JBuilder中執行[Open Project…]開啟由Maven產生的MyWebApp.jpx,無需調整任何設定即可進行專案開發,參考圖 4


設定 1 maven-jbuilder-plugin-1.5/plugin.jelly

<goal name="jbuilder"

description="Generate JBuilder project files"

prereqs="jbuilder:generate-library, jbuilder:generate-project"/>

<def:taglib uri="jbuilder">

中間省略…

<def:tag name="getDefaultProject">

<j:set var="jbuilderDirs" value=".jbuilder2005,.jbuilderX,.jbuilder9,.jbuilder8,.jbuilder7,.jbuilder6,.jbuilder5,.jbuilder4,.jbuilder" />

<j:set var="${userHome}" value="null" scope="parent"/>

以下省略…


4 開啟專案的JBuilder IDE

Maven IDEA Plugin

介紹完二個主流IDE的設定之後,我們來看一下一個鮮為人知,但功能卻不俗的IDE開發工具IntelliJ IDEAIDEA無需像Eclipse必須自行加上一堆Plugin之後才能達到較完善的開發環境要求,預設的IDEA環境非常的友善,上手非常容易,並且相似於Eclipse,網路上有眾多的Plugin支援IDEA。另外在所有的IDE開發工具中IDEA擁有最強的Refactor功能,方便開發人員進行重構。


Maven IDEA Plugin可以協助建立IDEA開發環境,但必須注意到目前IDEA Plugin版本為1.6,若讀者系統中的版本較舊,可透過執行[maven plugin:download -DgroupId=maven -DartifactId=maven-idea-plugin -Dversion=1.6]進行下載。


執行maven idea後,啟動IDEA進行[Open Project…]開啟由Maven產生的MyWebApp.ipr無需調整任何設定即可進行專案開發,參考圖 5


5 開啟專案的IntelliJ IDEA IDE

結語

在本期中介紹了如何使用Maven起始一個專案的建置,也了解了如何透過IDE PluginMaven的專案導到各個主流的IDE開發環境中進行協同開發,從這些實作中我們了解到,Maven並不會在開發中完全取代IDE或者限制使用特定IDE,而是以一種通用的專案建置方式協助每一個專案的進行。開發人員可以使用任何方便的IDE工具進行開發,而專案的建置、管理、測試、佈署等,一切的一切交給Maven就搞定了。

Read More......

2007年6月25日 星期一

(2006.04月號-147期)_實踐專案管理,使用Maven

在小型專案與一般於企業中建置的J2EE專案中,經常因導入的AP Server平台的不同而統一使用特定的IDE開發環境,例如:IBMWebSphere使用WSADOracleOC4J使用JDeveloperWebLogic使用Workshop等。

當進行大型專案開發,並分割出多數的系統模組都委外進行開發時,每一間委外廠商可能都使用不同的開發工具與IDE進行開發,因此如何管理、建置、佈署專案就是一門學問了。



而以往J2EE專案依IDE開發環境不同有不同的建置與佈署邏輯,而這些建置與佈署方式彼此並不相容,例如在WSAD建置的設定,無法移植至JDeveloperWorkshop使用,反之亦然,這對日後的維護上也將更形困難,我們沒辦法保證,當時開發時的IDE與目前維護時使用的IDE開發工具是相同的。


若欲將各模組委外進行開發,管理跨J2EE平台建置的大型專案,Ant是個很好的選擇,Ant可以撰寫腳本進行所有動作,要求委外廠商進行模組開發使用Ant也不是問題,因為目前業界大多數也是如此進行開發的。


但問題在於其Ant建構人員必須非常熟悉專案建置邏輯,並且善用一些開放原始碼元件協同進行開發、測試與佈署,若只是單純使用Ant基本腳本功能的話,則頂多也只是將原先手動進行複製、刪除、編譯、封裝JARWAREAR的動作轉為自動化而已,實質上並沒有太大的意義,還不如撰寫批次檔進行自動化來得簡單方便。


遺憾的是,於Ant中協同其他元件進行Ant功能的擴充並沒有想像中的簡單,參考設定 1中可以發現,於Ant中欲使用Cactus進行容器測試,必須進行以下步驟。


步驟1:首先必須於init中設定cactus的擴充標籤runservertests

步驟2testwar中進行原始碼的編譯與封裝。

步驟3prepare.test.tomcat.40中複製出tomcat環境並進行佈署。

步驟4test.tomcat.40中使用runservertests擴充標籤執行start.tomcat.40啟動tomcat、執行test進行容器測試、最後執行stop.tomcat.40關閉tomcat


基本上,建構人員並不想建置這麼複雜的設定、不想引入這些與專案原始碼無關並且只供測試用的jar檔、不想了解tomcat伺服容器的結構,更不用說怎麼去啟動與關閉tomcat了。但若專案欲於Ant上使用Cactus,這一切似乎是無可避免的,一旦加入的擴充元件愈多,Ant腳本將愈來愈複雜,目前Ant中只加入了tomcat的設定,試想當專案需要於WebLogicResinJBoss進行測試時,撰寫的Ant的腳本檔將會有多複雜與難以維護。


另外,在設定 1中我們也發現,多個專案中運行Cactus時的設定都是大同小異的,因此可以將其抽出作為template,並以ENTITY方式進行引入,於多個專案中使用。這是個很好的解決方案,解決了設定上的複雜性,卻還是免不了必須在專案中放置相依的jar檔,當建構範圍愈來愈大相依jar檔的管理也更加麻煩。更何況Ant中只包含建置邏輯的腳本,雖然目前在各IDE開發環境中幾乎都已支援Ant,但Ant並無法反向建置出各IDE開發環境的設定。


當使用版本控制程式進行管理時,相依jar檔愈多將使納入版本管理的程式愈顯龐大,但實際上該納入控管的只有原始碼而已,所有jar檔應該抽出並且共用才對。而若將IDE開發環境的設定納入控管則會造成災難,開發人員都會希望自己的開發環境是方便且友善的,為了如此的目的多多少少都會修改其相關設定,當每一位開發人員都有權利對IDE設定進行commit時,我們將發現開發環境的設定將會是一團亂,甚至於造成專案無法正常開啟。


總結上面所述,跨J2EE平台的專案管理工具最好能符合以下條件。


  • 由建置設定檔反向產生個別IDE開發環境設定,並且由建置人員將IDE設定檔排除在版本控制之外。

  • 將所有相依的jar檔統一放置於貯藏庫(repository)中,於建置專案的同時進行連結與Classpath的引用。

  • 將所有經常使用的建置邏輯抽出成為template,並強迫使用統一的建置邏輯,才不會因為不同的建置人員而寫出不同的建置腳本檔,而使日後維護更加艱難。

  • 將所有第三方協同專案開發的套件進行封裝,成為一個外掛模組。例如將Cactus封裝成為Plug-in,而之後只需簡易的呼叫Plug-in進行協同作業而無須理會原先該套件使用上有多複雜。


筆者於139期的RunPC中直接使用Maven進行了簡易專案開發的示範,從本期開始將與讀者一同分享,如何使用Maven完成上述的要求,並且更進一步的超越使其成為專案管理與建置工具的最佳實踐。

設定 1 Ant中使用Cactus設定

<target name="init">

<taskdef name="runservertests" classname="...">

[...設定cactus擴充標籤...]

</taskdef>

</target>

<target name="test.tomcat.40"

depends="prepare.test.tomcat.40">

<runservertests

testURL="[cactus測試網址]"

startTarget="start.tomcat.40"

stopTarget="stop.tomcat.40"

testTarget="test"/>

</target>

<target name="prepare.test.tomcat.40"

depends="check.test.tomcat.40,testwar">

[...設定Tomcat環境並將測試War檔進行佈署...]

<copy file="[...]/test.war"

tofile="[...]/webapps/test.war"/>

</target>

<target name="testwar" depends="compile">

<war warfile="..." webxml="...">

[...進行封裝War...]

</war>

</target>

<target name="start.tomcat.40">

<java classname="..." fork="yes">

[...設定Classpath並啟動tomcat...]

</java>

</target>

<target name="stop.tomcat.40">

<java classname="..." fork="yes">

[...設定Classpath並關閉tomcat...]

</java>

</target>

<target name="test">

<junit fork="yes">

<classpath>

[...設定cactus所需要的Classpath...]

</classpath>

<formatter type="plain" usefile="false"/>

<test name="[cactus測試單元]"/>

</junit>

</target>


Maven的起源

Ant曾經是筆者認為絕佳的建置工具,畢竟AntAnother Neat Tool(另一個很棒的工具)的縮寫。經過多次專案的歷練後發現,要建置與維護多個Ant腳本的專案將非常的不易。而Maven的起源最初是由於進行Jakarta Turbine專案,原先專案中每一個check inCVS的不同的JAR檔都擁有他們自己的build files,在進行維護上相當的困難,因此能夠進行很簡單的建置,並且定義出一個建置多個project的標準、一個對project構成清晰的描述、一個簡單的方式發佈project的資訊與一個能夠通過幾個不同的project去分享不同的JAR檔的方式就成為Maven這個專案的目標。


現在的Maven將可以協助開發人員更容易的進行日常工作的建立與更輕易的理解所有Java-based開發的專案。

因此可以在最短時間內使開發人員導入目前專案的開發。並且提供以下幾個特點。


  • 使專案建置的過程更加容易

  • 提供一個通用的專案建置系統

  • 提供建立高品質的專案開發資訊

  • 提供最佳化實踐的專案開發指南

  • 允許以通透的方式進行新功能的整合


理念上Maven是一個管理與整合專案開發的建置工具,在使用上基於XML概念的Project Object Model (POM-專案元件模型)的設計,並且實際上Maven的架構與核心是非常微小的,幾乎所有的功能都是由Plugins所提供,因此,在Maven中對於Plug-in的應用非常重要。


Maven1.x環境設定

Maven相容於大多數的OS平台,讀者可參考設定 2與設定 3進行Maven環境的指定並執行maven確認是否正確執行。


首次執行時,Maven預設將[MAVEN_HOME]/plugins下所有jar檔進行解壓縮放置於${user.home}/.maven/cache路徑下,在Windows中是置於C:Documents and Settings[登入帳號] .mavencache,於Linux則是置於/home/[登入帳號] /.maven/cache下,這些所謂的plugins大多是基於Jelly語言所定義的建置腳本,也將是日後我們最常使用的套件。


Maven對於相依jar檔的管理則提供了Remote & Local Repository進行集中的管理,預設Remote Repository指向http://www.ibiblio.org/maven網站而Local Repository置於${user.home}/.maven/ repository下,Maven運行過程中相依的檔案會經由Remote Repository下載至Local Repository存放。


pluginslocal repository置於${user.home}/.maven/cache${user.home}/.maven/ repository,原意在跨平台環境下都能指向開發人員登入後User Home的資料放置路徑,但通常在實際開發中我們會希望將其路徑指向特定位置。


設定 2 MavenWindows平台設定

set MAVEN_HOME=[maven放置路徑]maven
set PATH=%MAVEN_HOME%bin;%PATH%


設定 3 MavenLinux平台設定

export MAVEN_HOME= [maven放置路徑] /maven
export PATH=${PATH}:${MAVEN_HOME}/bin


Maven參數設定與優先權

在此我們可以參考圖 1進行參數的修改,該圖說明了Maven中參數優先權的順序,在Ant中當使用property標籤進行設定之後,將無法再次修改其參數值,而MavenAnt的差異在於Maven依照設定檔放置的位置不同擁有其不同的優先權。


讀者可自行解壓縮[MAVEN_HOME]/lib/maven.jar,當中可以發現名為defaults.properties,該檔為所有預設參數放置的位置。另外在我們建置的專案中皆可包含二個設定檔,分別為project.propertiesbuild.properties,並且build.properties優先權高於project.properties,也就是說當這二個檔案中使用相同參數時build.properties所設定的值會替代project.properties的值。但這二個檔案的用意究竟為何?一般初次接觸Maven的人都會搞混,觀念上來說project.properties所存的參數多為專案所需使用的參數,build.properties所設定的參數則為建置專案時所需的參數。


以另外一個觀點來說,假設於目前專案開發階段,我們將程式碼納入版本控管,並在一部測試伺服主機上進行自動化的每日構建與測試,則我們可以將這部測試機或正式機上所需的建置參數指定給project.properties並將該檔納入版本控管當中。因此在進行Daily Build時可以從project.properties取得參數。


而開發人員於自己的開發環境中進行check out原始碼後,一樣可以從版本控制系統中取得project.properties,但該檔的設定並不一定與目前開發人員的環境相同,因此建置上可能會發生問題,為此開發人員可自行建立build.properties並將設定上的不同處進行覆寫,來完成專案建置與測試。而build.properties我們通常不納入版本控管當中。另外Maven中協同多專案進行開發是非常容易的,所以目前進行開發的專案,有可能是父專案中的子專案,也因此繼承了父專案的相關屬性,在父專案下亦可包含project.propertiesbuild.properties二個設定檔。


User home的路徑下,也就是C:Documents and Settings[登入帳號],是屬於優先權次高的設定檔,通常在此進行對預設參數的覆寫,以取代原始的預設值,參考設定 4在這裏重新指定了maven.home.localmaven.repo.remote,透過maven.home.local參數的覆寫,相對的也指定了.maven/cache.maven/ repository的路徑,這一點可以從maven.jar中的defaults.properties的設定maven.repo.local=${maven.home.local}/repositorymaven.plugin.unpacked.dir = ${maven.home.local}/cache找到解答。


若為了加速下載速度或是企業內部網路受限的環境下而必須設定代理伺服器可使用設定 4maven.proxy相關參數進行設定,該設定中亦支援NTLM授權。但在網路管制較嚴格的企業中,代理伺服器的設定可能還是無法使Maven順利進行檔案下載作業,則這時必須求助於網管人員調整其網路權限。


使用Command line方式進行設定的參數將覆蓋之前所有的設定,參考設定 6中我們運行了plugin:download並覆寫參數maven.repo.remote以及其他參數。使用方式同java參數設定-Dproperty=value,擁有最高的優先權。


設定 4 User home下的build.properties

#設定cacherepository放置路徑

maven.home.local=${maven.home}/.maven


#設定remote repository網址

maven.repo.remote=file:E:/maven/maven.remote.repository,

http://www.ibiblio.org/maven,

http://maven-plugins.sourceforge.net

#Maven使用代理伺服器

maven.proxy.host=[代理伺服器網址]

maven.proxy.port=[代理伺服器Port]

maven.proxy.username=[帳號]

maven.proxy.password=[密碼]

maven.proxy.ntlm.username=[帳號]

maven.proxy.ntlm.password=[密碼]


1 參數優先權設定順序


資料結構、groupIdartifactId

Maven提供了許多的Plug-in,讓專案建構變得非常簡單。但Maven對於建置專案也有限制,其限制就是每個專案﹙在Maven中一個專案是指帶有project.xml的一個資料夾﹚只能生成一個成品(artifact)。但這個限制對於複雜的專案架構上並沒有問題,因為Maven支援多專案的建置。一個Maven專案可以由子專案,每個子專案可以建置產生自己的成品,最頂層的專案可以將子專案的成品打包進行封裝生成JARWAREAR。並且Maven在專案之間還提供了繼承(inheritance)。


Maven使用了groupIdartifactId的觀念,參考資料結構 1,當進行小型專案的建置時,我們只需要類似artifact-id-implementartifact-id-war簡易的建出JARWAR即可,這時groupIdartifactId通常是相同的。但若進行大型專案的建置,將可能將企業邏輯進行抽像化,產生介面、實作與其共用元件,而多個介面、實作、共用元件配合WAREAR的建置就可對大型專案進行封裝與佈署,如此的分層架構規劃下,許多Maven建置的artifact都將可以輕易的在各專案的建置下重覆使用,而這時的groupIdartifactId就必須有不同的名稱。例如Spring這個Framework,其groupIdorg.springframework,而分別依功能區分了spring-aopspring-beansspring-contextspring-dao…等的artifactId


註:關於project.xmlgroupIdartifactId的設定請參考139期的Run PC


資料結構 1 Maven資料夾結構

group-id

|-- artifact-id-interface

| |-- src/

| | |-- main/

| | | |-- java/

| | | | `-- ...

| | | `-- resources/

| | | `-- ...

| | `-- test/

| | |-- java/

| | | `-- ...

| | `-- resources/

| | `-- ...

| |-- project.xml

| |-- maven.xml

| |-- project.properties

| `-- build.properties

|

|-- artifact-id-implement

| `-- ...結構同上...

|

|-- artifact-id-war

| |-- src/

| | |-- java/

| | |-- resources/

| | `-- webapp/

| |-- test/

| | |-- java/

| | `-- resources/

| |-- project.xml

| |-- maven.xml

| |-- project.properties

| `-- build.properties

|

|-- project.xml

|-- maven.xml

|-- project.properties

`-- build.properties


建置Maven的組件與其核心架構

Maven的基本概念就是Project,每一個目錄包含有project.xml的目錄都是一個Project,而另一個概念就是Repository。當運行maven命令時,將會從Remote repository中複製於project.xmldependency標籤所相依的jar檔到Local repository,然後建構專案,封裝成品。參考設定 5與圖 2MavengroupId建立一個子目錄,在groupId資料夾中依所需的artifactId進行下載指定type的檔案,這裏所指的typejarejbplugin…等設定,若未指定則預設為jar。在設定 5的範例中將會建立groupIdorg.springframework的資料夾並於預設typejar的條件下建立jars資料夾並下載指定規則為<artifactid>-<version>.<type>spring-aop-1.2.6.jarspring-beans-1.2.6.jarspring-context-1.2.6.jar三個檔案於該資料夾內。


Repository的角色是相當明顯的,每個專案不再需要各自包含自己所依賴的第三方套件,Repository將會協助開發人員在多個專案間共用所有套件。而另一方面每個專案也可以自己建置成品,依Maven的觀念可使用install將套件安裝到Local repository,使用deploy將套件佈署到Remote repository。使用這種方式可以幫助開發者用一種標準的方式在專案間共用自製的套件。並且相互依賴的進行專案間持續整合(continuous integration)。若透過Cruise Control工具。專案可以持續的建置,最新釋出的成品可以重覆佈署到Repository中。在建置主機上運行持續性整合,將會定時的持續建構、發佈成品到應用伺服器中、進行自動測試並檢驗建置狀態。參考圖 3顯示了在Maven建置過程中project.xmlRepositorygoalplug-in各自的角色。


設定 5 project.xml設定dependency

<dependencies>

...

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>1.2.6</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>1.2.6</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>1.2.6</version>

</dependency>

...

</dependencies>



2 Repository資料夾結構


3 建置Maven相關組件


Maven Plug-in

Maven中幾乎所有功能都是由Plug-in所提供的,在Plug-in中包含了多個goal,而針對每個goal可以因應多個property的設定呈現不同的實作結果。例如Maven Jar Plug-in中原先在執行maven jar:jar這個goal之後,將進行原始碼編譯、測試並依artifactid-version.jar規則封裝成jar檔,而當我們在進行參數設定並加入maven.jar.final.name=xxxx.jar後重新執行,則最後封裝結果的檔案將會是xxxx.jar


究竟目前在本機上有多少的Plug-in可用,在此我們可以執行maven -g列出所有的Plug-in,亦可以使用maven -g > goal.txt重導至檔案中慢慢查看,若已經知道欲使用的Plug-in卻不知該Plug-in提供多少goal,可使用maven -P [plugin name]則可列出指定的Plug-in中所有的goalmaven -g maven -P 列出格式參考資料結構 2



Maven -g-P參數可以清楚的知道有多少的goal可使用,但Plug-in中有多少property就沒有參數可以協助列出以供查看了,關於這個部分可以連至Maven官方網站http://maven.apache.org/maven-1.x/reference/plugins參考詳細的Plug-in說明文件。



Maven在預先安裝中就已經包含了大多數的Plug-in了,但若欲使用的Plug-inbug或欲使用的功能於本機上的Plug-in尚未提供,則可至Maven官方網站中任選以下方式進行下載並安裝,。


  • 方式1:手動下載指定版本Plug-injar,置於[MAVEN_HOME]/plugins下即可。

  • 方式2:參考設定 6執行plugin:download並指定所需的Plug-in進行手動下載與安裝。

  • 方式3:參考設定 7執行maven任一goal的同時進行Plug-in進行自動下載與安裝。


註:Maven中的goalAnt腳本中的target是意義相同的,所指的都是預先定義好的任務,而Mavengoal是由Jelly(基於XML的標記語言)編寫的一系列命令腳本,而使用goal的語法為maven [plugin name]:[goal name]


資料結構 2 maven -g maven -P 輸出格式

__ __

| / |__ _Apache__ ___

| |/| / _` V / -_) ' ~ intelligent projects ~

|_| |___,_|_/___|_||_| v. 1.0.2


Available [Plugins] / Goals

===========================

[plugin name1] 針對plugin name1的描述

goal name1 ..... goal name1使用描述

goal name2 ..... goal name2使用描述

goal name3 ..... goal name3使用描述

...

[plugin name2] 針對plugin name2的描述

...


設定 6 Maven手動下載Plug-in

maven plugin:download

-Dmaven.repo.remote=http://www.ibiblio.org/maven, http://cvs.apache.org/repository/

-DgroupId=maven

-DartifactId=maven-jar-plugin

-Dversion=1.7


設定 7 MavenPOM中設定自動下載Plug-in

<dependencies>

...

<dependency>

<groupId> maven</groupId>

<artifactId> maven-jar-plugin </artifactId>

<version>1.7</version>

<type>plugin</type>

</dependency>

...

</dependencies>


Maven Goal

除了之前提過的project.xmlRepositoryPlug-in之外,在圖 3中我們可以看到maven.xml檔的設置,這個檔案主要用途在於延伸、擴展與客製化Goal,並且在這個檔案中我們會使用到preGoalgoalpostGoal三個標籤進行Goal的定義。


參考設定 8project標籤的default屬性設定為buildWeb,在Maven中的goalAnt中的target是相同的概念,而且Maven中的default屬性的設定亦是相同於Ant。這個設定意味著在目前專案的目錄下,直接執行maven指令,這時將會執行的defaultgoalbuildWeb,在這個設定中buildWeb直接使用attainGoal標籤去執行另外一個goalwar Plug-in中的war這個goal


從這個例子中我們可知,我們可以在goal標籤中撰寫所需的執行的Jelly腳本,當然這個腳本目前也相容於Ant的寫法,並且在goal中還可以使用attainGoal去執行其他Plug-ingoal。例如war:war這個goal依順序也會去執行java:compiletest:testwar:webapp…這幾個goal


preGoal標籤則代表了在指定的goal執行之前所要執行的建構規則,如之前所提war:war運行過程中會去執行java:compile,而本例中在編譯之前將先行呼叫xdoclet:webdoclet這個goal協助在建置過程中產生web相關的設定檔。因此使用preGoal進行功能的擴展就無須直接修改java:compile這個goal


同樣的postGoal的意義在於將在goal執行之後所運行的建構規則,在本例中當war:war完成後將產生一個war檔,透過postGoal功能的延伸的撰寫我們就可以進行簡易的tomcat的佈署動作了。


Maven中無論是goalpreGoalpostGoal都可以嵌入任何Anttask,。雖然給了Maven極大的靈活性,但是也很容易造成誤用,反而將之前使用Ant進行的設定、建置、佈署動作方式原封不動的搬到Maven來了,也就是說,只是在Maven的結構下依然故我的撰寫Ant的腳本,而造成在Maven下卻完全用不到Plug-in的好處。


建議在一般情況下,當找不到任何適當的的Plug-in時再自行撰寫,畢竟Maven最大的好處在於有無數的Plug-in可以使用,並且每一個Plug-in都是開放原始碼的心血結晶。


設定 8 maven.xml

<?xml version="1.0" encoding="Big5"?>

<project xmlns:j="jelly:core" xmlns:ant="jelly:ant" default="buildWeb">

<preGoal name="java:compile">

<attainGoal name="xdoclet:webdoclet" />

...在指定的goal之前執行建構規則...

</preGoal>

<goal name="buildWeb">

<attainGoal name="war:war" />

...建立一個新的或覆寫一個goal...

</goal>

<postGoal name="war:war">

<copy file="${maven.build.dir}/${pom.artifactId}.war"

todir="${tomcat.home.webapps}"/>

...在指定的goal之後執行建構規則...

</postGoal>

</project>


Maven1.xMaven2.x的差異

目前上過Maven官方網站的讀者應該已經注意到,Maven2已經Release了,雖然Maven2Maven1在概念上是非常相近的,但Maven2在架構上是對於Maven1完全的重寫,也因此將完全不相容。Maven1 Plug-in的部分也無法直接在Maven2上使用,而且因為效能考量下原先在Maven1建立Plug-in受到重用的Jelly語言,將不會在Maven2中出現,取而代之的將是回歸使用Java進行Plug-in的開發。


讀者看到這裏是否覺得,即然如此這時專案的開發應該全數移轉到Maven2上才是,也該放棄Maven1了。這也是當筆者看到Maven2的感想,但是實際上運作幾個Project後發現,雖然Maven2提供了不少相對於Maven1增加的新功能與特色,而且目前版本也尚稱穩定,但它最大的問題在於可用的Plug-in還不多,而少數的Plug-in目前還有Bug


這是理所當然的,想當初Maven1也是經過二、三年的時間才延伸出這些為數可觀又好用的Plug-in,若要Maven2Plug-in能夠成長到一個地步則還需要一段時間,但讀者無需擔心,目前Maven1建置的資料夾結構移到Maven2還是可以共用的,若日後需移轉到Maven2時,只需加上pom.xml並依Maven2規範設定就可以完成相容於Maven1Maven2的專案了。


相關資源

1. http://maven.apache.org/ Maven官方網站

2. http://www.theserverside.com/articles/article.tss?l=MavenMagic

Srikanth Shenoy TheServerSide.com發表的MavenMagic文章

3. Maven: A Developer's Notebook

By Vincent Massol, Timothy M. O'Brien


Read More......